query - gorm outside grails
¿Cómo garantizar la integridad de los datos cuando se usa Table Per Subclass? (2)
Hibernate garantiza la integridad de los datos en el caso de la tabla por subclase. En el caso de la tabla por subclase, la subclase mantiene una asociación de clave primaria con la superclase. Eche un vistazo a Hibernate Table por subclase . Para validar el hecho, aquí está su caso de prueba:
class Product {
String productCode
static mapping = {
tablePerHierarchy false
}
}
class Book extends Product{
String isbn
}
//Test Case
def testTablePerSubclass{
def product = new Product(productCode: ''XYZ456'')
product.save(flush: true, failOnError: true)
def book = new Book(isbn: ''123456123'', productCode: ''ABC123'')
book.save(flush: true, failOnError: true)
assert Book.list().size() == 1 //One Book
assert Book.list()*.id == [2] //Book id
assert Product.list().size() == 2 //One Product, one Book (2 Products)
assert Product.list()*.id == [1, 2] //Product id, Book Id
//Grab the product (book) to delete
def productToDelete = Product.get(book.id)
productToDelete.delete(flush: true)
assert Book.list().isEmpty() //Book deleted from Book table as well
assert Product.list().size() == 1 //One Product remaining in Product table
assert Product.list()*.id == [1] //Remaining Product Id
}
Mantenga logSql
verdadero en DataSource.groovy
para ver cómo se ejecutan los sqls correspondientes.
Log Sql Output:-
Hibernate: insert into product (id, version, product_code) values (null, ?, ?)
Hibernate: insert into product (id, version, product_code) values (null, ?, ?)
Hibernate: insert into book (isbn, id) values (?, ?)
Hibernate: select this_.id as id0_0_, this_1_.version as version0_0_, this_1_.product_code as product3_0_0_, this_.isbn as isbn1_0_ from book this_ inner join product this_1_ on this_.id=this_1_.id
[com.example.Book : 2]
Hibernate: select this_.id as id0_0_, this_.version as version0_0_, this_.product_code as product3_0_0_, this_1_.isbn as isbn1_0_, case when this_1_.id is not null then 1 when this_.id is not null then 0 end as clazz_0_ from product this_ left outer join book this_1_ on this_.id=this_1_.id
[com.example.Product : 1, com.example.Book : 2]
Hibernate: delete from book where id=?
Hibernate: delete from product where id=? and version=?
Hibernate: select this_.id as id0_0_, this_1_.version as version0_0_, this_1_.product_code as product3_0_0_, this_.isbn as isbn1_0_ from book this_ inner join product this_1_ on this_.id=this_1_.id
[]
Hibernate: select this_.id as id0_0_, this_.version as version0_0_, this_.product_code as product3_0_0_, this_1_.isbn as isbn1_0_, case when this_1_.id is not null then 1 when this_.id is not null then 0 end as clazz_0_ from product this_ left outer join book this_1_ on this_.id=this_1_.id
[com.example.Product : 1]
Usando Grails 2.2.2
Estoy utilizando la tabla por estrategia de subclase en Grails estableciendo la propiedad tablePerHierarchy
del campo de mapping
estática en mi superclase en falso. De esta forma, Grails crea una tabla para mi superclase y una tabla adicional para cada una de mis subclases.
Sin embargo, aunque los registros de superclase y subclase comparten el mismo ID (clave principal), no hay restricciones de clave externa para mantenerlos constantes, es decir, es posible eliminar el registro de superclase, dejando el registro de subclase en un estado no válido. Quiero saber si hay una configuración / propiedad para hacer que GORM aborde esto de alguna manera, por ejemplo, mediante restricciones. ¿O es mi única opción agregar manualmente claves foráneas?
Por ejemplo, dada la siguiente clase de dominio como superclase:
class Product {
String productCode
static mapping = {
tablePerHierarchy false
}
}
Y la siguiente clase de dominio como subclase:
class Book extends Product {
String isbn
}
Esto resulta en la creación de dos tablas, la tabla Product
y la tabla Book
. Al crear un libro, por ejemplo, a través de páginas con scaffolded, se inserta un registro en cada tabla, siendo su único vínculo el hecho de que el valor de ID sea el mismo para cada uno. Específicamente, los datos pueden verse así:
PRODUCT
Id Version ProductCode
1 1 BLAH-02X1
BOOK
Id ISBN
1 123-4-56-7891011-1
Debido a que no existe una relación formal definida en el nivel de la base de datos para estas tablas, es posible eliminar uno de los registros y dejar el otro, lo que da como resultado datos no válidos. Obviamente, puedo usar SQL para crear manualmente una restricción de clave externa en los dos campos de ID, pero esperaba dejar que Grails manejara eso. es posible?
Usando Grails 2.2.1
Resuelto
La siguiente solución me solucionó este problema. Agregue la clase a continuación a src/java
(esta clase no se puede escribir en Groovy)
package org.example;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.MappingException;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import java.util.Iterator;
public class TablePerSubclassConfiguration extends GrailsAnnotationConfiguration {
private static final long serialVersionUID = 1;
private boolean alreadyProcessed = false;
@Override
protected void secondPassCompile() throws MappingException {
super.secondPassCompile();
if (alreadyProcessed) {
return;
}
for (PersistentClass persistentClass : classes.values()) {
if (persistentClass instanceof RootClass) {
RootClass rootClass = (RootClass) persistentClass;
if (rootClass.hasSubclasses()) {
Iterator subclasses = rootClass.getSubclassIterator();
while (subclasses.hasNext()) {
Object subclass = subclasses.next();
// This test ensures that foreign keys will only be created for subclasses that are
// mapped using "table per subclass"
if (subclass instanceof JoinedSubclass) {
JoinedSubclass joinedSubclass = (JoinedSubclass) subclass;
joinedSubclass.createForeignKey();
}
}
}
}
}
alreadyProcessed = true;
}
}
Luego en DataSource.groovy
establece esto como la clase de configuración
dataSource {
configClass = ''org.example.TablePerSubclassConfiguration''
pooled = true
driverClassName = "org.h2.Driver"
username = "sa"
password = ""
dbCreate = "update"
url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
Actualizar
He enviado una solicitud de extracción a Grails para este problema. La solución se incluyó en Grails 2.3.8 o 2.3.9 (no recuerdo cuál).