/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2006-2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.hql;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.hql.internal.ast.HqlSqlWalker;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.persister.entity.EntityPersister;
import org.junit.Test;
import junit.framework.AssertionFailedError;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.SkipLog;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Tests execution of bulk UPDATE/DELETE statements through the new AST parser.
*
* @author Steve Ebersole
*/
public class BulkManipulationTest extends BaseCoreFunctionalTestCase {
public String[] getMappings() {
return new String[] {
"hql/Animal.hbm.xml",
"hql/Vehicle.hbm.xml",
"hql/KeyManyToOneEntity.hbm.xml",
"hql/Versions.hbm.xml",
"hql/FooBarCopy.hbm.xml",
"legacy/Multi.hbm.xml",
"hql/EntityWithCrazyCompositeKey.hbm.xml",
"hql/SimpleEntityWithAssociation.hbm.xml",
"hql/BooleanLiteralEntity.hbm.xml"
};
}
@Test
public void testDeleteNonExistentEntity() {
Session s = openSession();
Transaction t = s.beginTransaction();
try {
s.createQuery( "delete NonExistentEntity" ).executeUpdate();
fail( "no exception thrown" );
}
catch( QueryException ignore ) {
}
t.commit();
s.close();
}
@Test
public void testUpdateNonExistentEntity() {
Session s = openSession();
Transaction t = s.beginTransaction();
try {
s.createQuery( "update NonExistentEntity e set e.someProp = ?" ).executeUpdate();
fail( "no exception thrown" );
}
catch( QueryException e ) {
}
t.commit();
s.close();
}
@Test
public void testTempTableGenerationIsolation() throws Throwable{
Session s = openSession();
s.beginTransaction();
Truck truck = new Truck();
truck.setVin( "123t" );
truck.setOwner( "Steve" );
s.save( truck );
// manually flush the session to ensure the insert happens
s.flush();
// now issue a bulk delete against Car which should force the temp table to be
// created. we need to test to ensure that this does not cause the transaction
// to be committed...
s.createQuery( "delete from Vehicle" ).executeUpdate();
s.getTransaction().rollback();
s.close();
s = openSession();
s.beginTransaction();
List list = s.createQuery( "from Car" ).list();
assertEquals( "temp table gen caused premature commit", 0, list.size() );
s.createQuery( "delete from Car" ).executeUpdate();
s.getTransaction().rollback();
s.close();
}
@Test
public void testBooleanHandling() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
// currently, we need the three different binds because they are different underlying types...
int count = s.createQuery( "update BooleanLiteralEntity set yesNoBoolean = :b1, trueFalseBoolean = :b2, zeroOneBoolean = :b3" )
.setBoolean( "b1", true )
.setBoolean( "b2", true )
.setBoolean( "b3", true )
.executeUpdate();
assertEquals( 1, count );
BooleanLiteralEntity entity = ( BooleanLiteralEntity ) s.createQuery( "from BooleanLiteralEntity" ).uniqueResult();
assertTrue( entity.isYesNoBoolean() );
assertTrue( entity.isTrueFalseBoolean() );
assertTrue( entity.isZeroOneBoolean() );
s.clear();
count = s.createQuery( "update BooleanLiteralEntity set yesNoBoolean = true, trueFalseBoolean = true, zeroOneBoolean = true" )
.executeUpdate();
assertEquals( 1, count );
entity = ( BooleanLiteralEntity ) s.createQuery( "from BooleanLiteralEntity" ).uniqueResult();
assertTrue( entity.isYesNoBoolean() );
assertTrue( entity.isTrueFalseBoolean() );
assertTrue( entity.isZeroOneBoolean() );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testSimpleInsert() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
s.createQuery( "insert into Pickup (id, vin, owner) select id, vin, owner from Car" ).executeUpdate();
t.commit();
t = s.beginTransaction();
s.createQuery( "delete Vehicle" ).executeUpdate();
t.commit();
s.close();
data.cleanup();
}
@Test
public void testSimpleNativeSQLInsert() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
List l = s.createQuery("from Vehicle").list();
assertEquals(l.size(),4);
s.createSQLQuery( "insert into Pickup (id, vin, owner) select id, vin, owner from Car" ).executeUpdate();
l = s.createQuery("from Vehicle").list();
assertEquals( l.size(), 5 );
t.commit();
t = s.beginTransaction();
s.createSQLQuery( "delete from Truck" ).executeUpdate();
l = s.createQuery("from Vehicle").list();
assertEquals(l.size(),4);
Car c = (Car) s.createQuery( "from Car where owner = 'Kirsten'" ).uniqueResult();
c.setOwner( "NotKirsten" );
assertEquals( 0, s.getNamedQuery( "native-delete-car" ).setString( 0, "Kirsten" ).executeUpdate() );
assertEquals( 1, s.getNamedQuery( "native-delete-car" ).setString( 0, "NotKirsten" ).executeUpdate() );
assertEquals(
0, s.createSQLQuery( "delete from SUV where owner = :owner" )
.setString( "owner", "NotThere" )
.executeUpdate()
);
assertEquals(
1, s.createSQLQuery( "delete from SUV where owner = :owner" )
.setString( "owner", "Joe" )
.executeUpdate()
);
s.createSQLQuery( "delete from Pickup" ).executeUpdate();
l = s.createQuery("from Vehicle").list();
assertEquals(l.size(),0);
t.commit();
s.close();
data.cleanup();
}
@Test
public void testInsertWithManyToOne() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
s.createQuery( "insert into Animal (description, bodyWeight, mother) select description, bodyWeight, mother from Human" ).executeUpdate();
t.commit();
t = s.beginTransaction();
t.commit();
s.close();
data.cleanup();
}
@Test
public void testInsertWithMismatchedTypes() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
try {
s.createQuery( "insert into Pickup (owner, vin, id) select id, vin, owner from Car" ).executeUpdate();
fail( "mismatched types did not error" );
}
catch( QueryException e ) {
// expected result
}
t.commit();
t = s.beginTransaction();
s.createQuery( "delete Vehicle" ).executeUpdate();
t.commit();
s.close();
data.cleanup();
}
@Test
public void testInsertIntoSuperclassPropertiesFails() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
try {
s.createQuery( "insert into Human (id, bodyWeight) select id, bodyWeight from Lizard" ).executeUpdate();
fail( "superclass prop insertion did not error" );
}
catch( QueryException e ) {
// expected result
}
t.commit();
t = s.beginTransaction();
s.createQuery( "delete Animal where mother is not null" ).executeUpdate();
s.createQuery( "delete Animal where father is not null" ).executeUpdate();
s.createQuery( "delete Animal" ).executeUpdate();
t.commit();
s.close();
data.cleanup();
}
@Test
public void testInsertAcrossMappedJoinFails() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
try {
s.createQuery( "insert into Joiner (name, joinedName) select vin, owner from Car" ).executeUpdate();
fail( "mapped-join insertion did not error" );
}
catch( QueryException e ) {
// expected result
}
t.commit();
t = s.beginTransaction();
s.createQuery( "delete Joiner" ).executeUpdate();
s.createQuery( "delete Vehicle" ).executeUpdate();
t.commit();
s.close();
data.cleanup();
}
protected boolean supportsBulkInsertIdGeneration(Class entityClass) {
EntityPersister persister = sessionFactory().getEntityPersister( entityClass.getName() );
IdentifierGenerator generator = persister.getIdentifierGenerator();
return HqlSqlWalker.supportsIdGenWithBulkInsertion( generator );
}
@Test
public void testInsertWithGeneratedId() {
// Make sure the env supports bulk inserts with generated ids...
if ( !supportsBulkInsertIdGeneration( PettingZoo.class ) ) {
SkipLog.reportSkip(
"bulk id generation not supported",
"test bulk inserts with generated id and generated timestamp"
);
return;
}
// create a Zoo
Zoo zoo = new Zoo();
zoo.setName( "zoo" );
Session s = openSession();
Transaction t = s.beginTransaction();
s.save( zoo );
t.commit();
s.close();
s = openSession();
t = s.beginTransaction();
int count = s.createQuery( "insert into PettingZoo (name) select name from Zoo" ).executeUpdate();
t.commit();
s.close();
assertEquals( "unexpected insertion count", 1, count );
s = openSession();
t = s.beginTransaction();
PettingZoo pz = ( PettingZoo ) s.createQuery( "from PettingZoo" ).uniqueResult();
t.commit();
s.close();
assertEquals( zoo.getName(), pz.getName() );
assertTrue( !zoo.getId().equals( pz.getId() ) );
s = openSession();
t = s.beginTransaction();
s.createQuery( "delete Zoo" ).executeUpdate();
t.commit();
s.close();
}
@SuppressWarnings( {"UnnecessaryUnboxing"})
@Test
public void testInsertWithGeneratedVersionAndId() {
// Make sure the env supports bulk inserts with generated ids...
if ( !supportsBulkInsertIdGeneration( IntegerVersioned.class ) ) {
SkipLog.reportSkip(
"bulk id generation not supported",
"test bulk inserts with generated id and generated timestamp"
);
return;
}
Session s = openSession();
Transaction t = s.beginTransaction();
IntegerVersioned entity = new IntegerVersioned( "int-vers" );
s.save( entity );
s.createQuery( "select id, name, version from IntegerVersioned" ).list();
t.commit();
s.close();
Long initialId = entity.getId();
int initialVersion = entity.getVersion();
s = openSession();
t = s.beginTransaction();
int count = s.createQuery( "insert into IntegerVersioned ( name ) select name from IntegerVersioned" ).executeUpdate();
t.commit();
s.close();
assertEquals( "unexpected insertion count", 1, count );
s = openSession();
t = s.beginTransaction();
IntegerVersioned created = ( IntegerVersioned ) s.createQuery( "from IntegerVersioned where id <> :initialId" )
.setLong( "initialId", initialId.longValue() )
.uniqueResult();
t.commit();
s.close();
assertEquals( "version was not seeded", initialVersion, created.getVersion() );
s = openSession();
t = s.beginTransaction();
s.createQuery( "delete IntegerVersioned" ).executeUpdate();
t.commit();
s.close();
}
@Test
@SuppressWarnings( {"UnnecessaryUnboxing"})
@RequiresDialectFeature(
value = DialectChecks.SupportsParametersInInsertSelectCheck.class,
comment = "dialect does not support parameter in INSERT ... SELECT"
)
public void testInsertWithGeneratedTimestampVersion() {
// Make sure the env supports bulk inserts with generated ids...
if ( !supportsBulkInsertIdGeneration( TimestampVersioned.class ) ) {
SkipLog.reportSkip(
"bulk id generation not supported",
"test bulk inserts with generated id and generated timestamp"
);
return;
}
Session s = openSession();
Transaction t = s.beginTransaction();
TimestampVersioned entity = new TimestampVersioned( "int-vers" );
s.save( entity );
s.createQuery( "select id, name, version from TimestampVersioned" ).list();
t.commit();
s.close();
Long initialId = entity.getId();
//Date initialVersion = entity.getVersion();
s = openSession();
t = s.beginTransaction();
int count = s.createQuery( "insert into TimestampVersioned ( name ) select name from TimestampVersioned" ).executeUpdate();
t.commit();
s.close();
assertEquals( "unexpected insertion count", 1, count );
s = openSession();
t = s.beginTransaction();
TimestampVersioned created = ( TimestampVersioned ) s.createQuery( "from TimestampVersioned where id <> :initialId" )
.setLong( "initialId", initialId.longValue() )
.uniqueResult();
t.commit();
s.close();
assertNotNull( created.getVersion() );
//assertEquals( "version was not seeded", initialVersion, created.getVersion() );
s = openSession();
t = s.beginTransaction();
s.createQuery( "delete TimestampVersioned" ).executeUpdate();
t.commit();
s.close();
}
@Test
public void testInsertWithSelectListUsingJoins() {
// this is just checking parsing and syntax...
Session s = openSession();
s.beginTransaction();
s.createQuery( "insert into Animal (description, bodyWeight) select h.description, h.bodyWeight from Human h where h.mother.mother is not null" ).executeUpdate();
s.createQuery( "insert into Animal (description, bodyWeight) select h.description, h.bodyWeight from Human h join h.mother m where m.mother is not null" ).executeUpdate();
s.createQuery( "delete from Animal" ).executeUpdate();
s.getTransaction().commit();
s.close();
}
@Test
public void testIncorrectSyntax() {
Session s = openSession();
Transaction t = s.beginTransaction();
try {
s.createQuery( "update Human set Human.description = 'xyz' where Human.id = 1 and Human.description is null" );
fail( "expected failure" );
}
catch( QueryException expected ) {
// ignore : expected behavior
}
t.commit();
s.close();
}
@SuppressWarnings( {"unchecked"})
@Test
public void testUpdateWithWhereExistsSubquery() {
// multi-table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Session s = openSession();
Transaction t = s.beginTransaction();
Human joe = new Human();
joe.setName( new Name( "Joe", 'Q', "Public" ) );
s.save( joe );
Human doll = new Human();
doll.setName( new Name( "Kyu", 'P', "Doll" ) );
doll.setFriends( new ArrayList() );
doll.getFriends().add( joe );
s.save( doll );
t.commit();
s.close();
s = openSession();
t = s.beginTransaction();
String updateQryString = "update Human h " +
"set h.description = 'updated' " +
"where exists (" +
" select f.id " +
" from h.friends f " +
" where f.name.last = 'Public' " +
")";
int count = s.createQuery( updateQryString ).executeUpdate();
assertEquals( 1, count );
s.delete( doll );
s.delete( joe );
t.commit();
s.close();
// single-table (one-to-many & many-to-many) ~~~~~~~~~~~~~~~~~~~~~~~~~~
s = openSession();
t = s.beginTransaction();
SimpleEntityWithAssociation entity = new SimpleEntityWithAssociation();
SimpleEntityWithAssociation other = new SimpleEntityWithAssociation();
entity.setName( "main" );
other.setName( "many-to-many-association" );
entity.getManyToManyAssociatedEntities().add( other );
entity.addAssociation( "one-to-many-association" );
s.save( entity );
t.commit();
s.close();
s = openSession();
t = s.beginTransaction();
// one-to-many test
updateQryString = "update SimpleEntityWithAssociation e " +
"set e.name = 'updated' " +
"where exists (" +
" select a.id " +
" from e.associatedEntities a " +
" where a.name = 'one-to-many-association' " +
")";
count = s.createQuery( updateQryString ).executeUpdate();
assertEquals( 1, count );
// many-to-many test
if ( getDialect().supportsSubqueryOnMutatingTable() ) {
updateQryString = "update SimpleEntityWithAssociation e " +
"set e.name = 'updated' " +
"where exists (" +
" select a.id " +
" from e.manyToManyAssociatedEntities a " +
" where a.name = 'many-to-many-association' " +
")";
count = s.createQuery( updateQryString ).executeUpdate();
assertEquals( 1, count );
}
s.delete( entity.getManyToManyAssociatedEntities().iterator().next() );
s.delete( entity );
t.commit();
s.close();
}
@Test
public void testIncrementCounterVersion() {
Session s = openSession();
Transaction t = s.beginTransaction();
IntegerVersioned entity = new IntegerVersioned( "int-vers" );
s.save( entity );
t.commit();
s.close();
int initialVersion = entity.getVersion();
s = openSession();
t = s.beginTransaction();
int count = s.createQuery( "update versioned IntegerVersioned set name = name" ).executeUpdate();
assertEquals( "incorrect exec count", 1, count );
t.commit();
t = s.beginTransaction();
entity = ( IntegerVersioned ) s.load( IntegerVersioned.class, entity.getId() );
assertEquals( "version not incremented", initialVersion + 1, entity.getVersion() );
s.delete( entity );
t.commit();
s.close();
}
@Test
public void testIncrementTimestampVersion() {
Session s = openSession();
Transaction t = s.beginTransaction();
TimestampVersioned entity = new TimestampVersioned( "ts-vers" );
s.save( entity );
t.commit();
s.close();
Date initialVersion = entity.getVersion();
synchronized (this) {
try {
wait(1500);
}
catch (InterruptedException ie) {}
}
s = openSession();
t = s.beginTransaction();
int count = s.createQuery( "update versioned TimestampVersioned set name = name" ).executeUpdate();
assertEquals( "incorrect exec count", 1, count );
t.commit();
t = s.beginTransaction();
entity = ( TimestampVersioned ) s.load( TimestampVersioned.class, entity.getId() );
assertTrue( "version not incremented", entity.getVersion().after( initialVersion ) );
s.delete( entity );
t.commit();
s.close();
}
@Test
@SuppressWarnings( {"UnnecessaryUnboxing"})
public void testUpdateOnComponent() {
Session s = openSession();
Transaction t = s.beginTransaction();
Human human = new Human();
human.setName( new Name( "Stevee", 'X', "Ebersole" ) );
s.save( human );
s.flush();
t.commit();
String correctName = "Steve";
t = s.beginTransaction();
int count = s.createQuery( "update Human set name.first = :correction where id = :id" )
.setString( "correction", correctName )
.setLong( "id", human.getId().longValue() )
.executeUpdate();
assertEquals( "Incorrect update count", 1, count );
t.commit();
t = s.beginTransaction();
s.refresh( human );
assertEquals( "Update did not execute properly", correctName, human.getName().getFirst() );
s.createQuery( "delete Human" ).executeUpdate();
t.commit();
s.close();
}
@Test
public void testUpdateOnManyToOne() {
Session s = openSession();
Transaction t = s.beginTransaction();
s.createQuery( "update Animal a set a.mother = null where a.id = 2" ).executeUpdate();
if ( ! ( getDialect() instanceof MySQLDialect ) ) {
// MySQL does not support (even un-correlated) subqueries against the update-mutating table
s.createQuery( "update Animal a set a.mother = (from Animal where id = 1) where a.id = 2" ).executeUpdate();
}
t.commit();
s.close();
}
@Test
public void testUpdateOnImplicitJoinFails() {
Session s = openSession();
Transaction t = s.beginTransaction();
Human human = new Human();
human.setName( new Name( "Steve", 'E', null ) );
Human mother = new Human();
mother.setName( new Name( "Jane", 'E', null ) );
human.setMother( mother );
s.save( human );
s.save( mother );
s.flush();
t.commit();
t = s.beginTransaction();
try {
s.createQuery( "update Human set mother.name.initial = :initial" ).setString( "initial", "F" ).executeUpdate();
fail( "update allowed across implicit join" );
}
catch( QueryException e ) {
}
s.createQuery( "delete Human where mother is not null" ).executeUpdate();
s.createQuery( "delete Human" ).executeUpdate();
t.commit();
s.close();
}
@Test
@SuppressWarnings( {"UnnecessaryUnboxing"})
public void testUpdateOnDiscriminatorSubclass() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "update PettingZoo set name = name" ).executeUpdate();
assertEquals( "Incorrect discrim subclass update count", 1, count );
t.rollback();
t = s.beginTransaction();
count = s.createQuery( "update PettingZoo pz set pz.name = pz.name where pz.id = :id" )
.setLong( "id", data.pettingZoo.getId().longValue() )
.executeUpdate();
assertEquals( "Incorrect discrim subclass update count", 1, count );
t.rollback();
t = s.beginTransaction();
count = s.createQuery( "update Zoo as z set z.name = z.name" ).executeUpdate();
assertEquals( "Incorrect discrim subclass update count", 2, count );
t.rollback();
t = s.beginTransaction();
// TODO : not so sure this should be allowed. Seems to me that if they specify an alias,
// property-refs should be required to be qualified.
count = s.createQuery( "update Zoo as z set name = name where id = :id" )
.setLong( "id", data.zoo.getId().longValue() )
.executeUpdate();
assertEquals( "Incorrect discrim subclass update count", 1, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testUpdateOnAnimal() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "update Animal set description = description where description = :desc" )
.setString( "desc", data.frog.getDescription() )
.executeUpdate();
assertEquals( "Incorrect entity-updated count", 1, count );
count = s.createQuery( "update Animal set description = :newDesc where description = :desc" )
.setString( "desc", data.polliwog.getDescription() )
.setString( "newDesc", "Tadpole" )
.executeUpdate();
assertEquals( "Incorrect entity-updated count", 1, count );
Animal tadpole = ( Animal ) s.load( Animal.class, data.polliwog.getId() );
assertEquals( "Update did not take effect", "Tadpole", tadpole.getDescription() );
count = s.createQuery( "update Animal set bodyWeight = bodyWeight + :w1 + :w2" )
.setDouble( "w1", 1 )
.setDouble( "w2", 2 )
.executeUpdate();
assertEquals( "incorrect count on 'complex' update assignment", count, 6 );
if ( ! ( getDialect() instanceof MySQLDialect ) ) {
// MySQL does not support (even un-correlated) subqueries against the update-mutating table
s.createQuery( "update Animal set bodyWeight = ( select max(bodyWeight) from Animal )" )
.executeUpdate();
}
t.commit();
s.close();
data.cleanup();
}
@Test
public void testUpdateOnMammal() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "update Mammal set description = description" ).executeUpdate();
assertEquals( "incorrect update count against 'middle' of joined-subclass hierarchy", 2, count );
count = s.createQuery( "update Mammal set bodyWeight = 25" ).executeUpdate();
assertEquals( "incorrect update count against 'middle' of joined-subclass hierarchy", 2, count );
if ( ! ( getDialect() instanceof MySQLDialect ) ) {
// MySQL does not support (even un-correlated) subqueries against the update-mutating table
count = s.createQuery( "update Mammal set bodyWeight = ( select max(bodyWeight) from Animal )" ).executeUpdate();
assertEquals( "incorrect update count against 'middle' of joined-subclass hierarchy", 2, count );
}
t.commit();
s.close();
data.cleanup();
}
@Test
public void testUpdateSetNullUnionSubclass() {
TestData data = new TestData();
data.prepare();
// These should reach out into *all* subclass tables...
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "update Vehicle set owner = 'Steve'" ).executeUpdate();
assertEquals( "incorrect restricted update count", 4, count );
count = s.createQuery( "update Vehicle set owner = null where owner = 'Steve'" ).executeUpdate();
assertEquals( "incorrect restricted update count", 4, count );
try {
count = s.createQuery( "delete Vehicle where owner is null" ).executeUpdate();
assertEquals( "incorrect restricted delete count", 4, count );
}
catch ( AssertionFailedError afe ) {
if ( H2Dialect.class.isInstance( getDialect() ) ) {
// http://groups.google.com/group/h2-database/t/5548ff9fd3abdb7
// this is fixed in H2 1.2.140
count = s.createQuery( "delete Vehicle" ).executeUpdate();
assertEquals( "incorrect count", 4, count );
}
else {
throw afe;
}
}
t.commit();
s.close();
data.cleanup();
}
@Test
public void testUpdateSetNullOnDiscriminatorSubclass() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "update PettingZoo set address.city = null" ).executeUpdate();
assertEquals( "Incorrect discrim subclass delete count", 1, count );
count = s.createQuery( "delete Zoo where address.city is null" ).executeUpdate();
assertEquals( "Incorrect discrim subclass delete count", 1, count );
count = s.createQuery( "update Zoo set address.city = null" ).executeUpdate();
assertEquals( "Incorrect discrim subclass delete count", 1, count );
count = s.createQuery( "delete Zoo where address.city is null" ).executeUpdate();
assertEquals( "Incorrect discrim subclass delete count", 1, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testUpdateSetNullOnJoinedSubclass() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "update Mammal set bodyWeight = null" ).executeUpdate();
assertEquals( "Incorrect deletion count on joined subclass", 2, count );
count = s.createQuery( "delete Animal where bodyWeight = null" ).executeUpdate();
assertEquals( "Incorrect deletion count on joined subclass", 2, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteWithSubquery() {
// setup the test data...
Session s = openSession();
s.beginTransaction();
SimpleEntityWithAssociation owner = new SimpleEntityWithAssociation( "myEntity-1" );
owner.addAssociation( "assoc-1" );
owner.addAssociation( "assoc-2" );
owner.addAssociation( "assoc-3" );
s.save( owner );
SimpleEntityWithAssociation owner2 = new SimpleEntityWithAssociation( "myEntity-2" );
owner2.addAssociation( "assoc-1" );
owner2.addAssociation( "assoc-2" );
owner2.addAssociation( "assoc-3" );
owner2.addAssociation( "assoc-4" );
s.save( owner2 );
SimpleEntityWithAssociation owner3 = new SimpleEntityWithAssociation( "myEntity-3" );
s.save( owner3 );
s.getTransaction().commit();
s.close();
// now try the bulk delete
s = openSession();
s.beginTransaction();
int count = s.createQuery( "delete SimpleEntityWithAssociation e where size( e.associatedEntities ) = 0 and e.name like '%'" ).executeUpdate();
assertEquals( "incorrect delete count", 1, count );
s.getTransaction().commit();
s.close();
// finally, clean up
s = openSession();
s.beginTransaction();
s.createQuery( "delete SimpleAssociatedEntity" ).executeUpdate();
s.createQuery( "delete SimpleEntityWithAssociation" ).executeUpdate();
s.getTransaction().commit();
s.close();
}
@Test
@SuppressWarnings( {"UnnecessaryUnboxing"})
@RequiresDialectFeature(
value = DialectChecks.HasSelfReferentialForeignKeyBugCheck.class,
comment = "self referential FK bug"
)
public void testSimpleDeleteOnAnimal() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "delete from Animal as a where a.id = :id" )
.setLong( "id", data.polliwog.getId().longValue() )
.executeUpdate();
assertEquals( "Incorrect delete count", 1, count );
count = s.createQuery( "delete Animal where id = :id" )
.setLong( "id", data.catepillar.getId().longValue() )
.executeUpdate();
assertEquals( "incorrect delete count", 1, count );
if ( getDialect().supportsSubqueryOnMutatingTable() ) {
count = s.createQuery( "delete from User u where u not in (select u from User u)" ).executeUpdate();
assertEquals( 0, count );
}
count = s.createQuery( "delete Animal a" ).executeUpdate();
assertEquals( "Incorrect delete count", 4, count );
List list = s.createQuery( "select a from Animal as a" ).list();
assertTrue( "table not empty", list.isEmpty() );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteOnDiscriminatorSubclass() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "delete PettingZoo" ).executeUpdate();
assertEquals( "Incorrect discrim subclass delete count", 1, count );
count = s.createQuery( "delete Zoo" ).executeUpdate();
assertEquals( "Incorrect discrim subclass delete count", 1, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteOnJoinedSubclass() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "delete Mammal where bodyWeight > 150" ).executeUpdate();
assertEquals( "Incorrect deletion count on joined subclass", 1, count );
count = s.createQuery( "delete Mammal" ).executeUpdate();
assertEquals( "Incorrect deletion count on joined subclass", 1, count );
count = s.createQuery( "delete SubMulti" ).executeUpdate();
assertEquals( "Incorrect deletion count on joined subclass", 0, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteOnMappedJoin() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "delete Joiner where joinedName = :joinedName" ).setString( "joinedName", "joined-name" ).executeUpdate();
assertEquals( "Incorrect deletion count on joined subclass", 1, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteUnionSubclassAbstractRoot() {
TestData data = new TestData();
data.prepare();
// These should reach out into *all* subclass tables...
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "delete Vehicle where owner = :owner" ).setString( "owner", "Steve" ).executeUpdate();
assertEquals( "incorrect restricted update count", 1, count );
count = s.createQuery( "delete Vehicle" ).executeUpdate();
assertEquals( "incorrect update count", 3, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteUnionSubclassConcreteSubclass() {
TestData data = new TestData();
data.prepare();
// These should only affect the given table
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "delete Truck where owner = :owner" ).setString( "owner", "Steve" ).executeUpdate();
assertEquals( "incorrect restricted update count", 1, count );
count = s.createQuery( "delete Truck" ).executeUpdate();
assertEquals( "incorrect update count", 2, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteUnionSubclassLeafSubclass() {
TestData data = new TestData();
data.prepare();
// These should only affect the given table
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "delete Car where owner = :owner" ).setString( "owner", "Kirsten" ).executeUpdate();
assertEquals( "incorrect restricted update count", 1, count );
count = s.createQuery( "delete Car" ).executeUpdate();
assertEquals( "incorrect update count", 0, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteWithMetadataWhereFragments() throws Throwable {
Session s = openSession();
Transaction t = s.beginTransaction();
// Note: we are just checking the syntax here...
s.createQuery("delete from Bar").executeUpdate();
s.createQuery("delete from Bar where barString = 's'").executeUpdate();
t.commit();
s.close();
}
@Test
public void testDeleteRestrictedOnManyToOne() {
TestData data = new TestData();
data.prepare();
Session s = openSession();
Transaction t = s.beginTransaction();
int count = s.createQuery( "delete Animal where mother = :mother" )
.setEntity( "mother", data.butterfly )
.executeUpdate();
assertEquals( 1, count );
t.commit();
s.close();
data.cleanup();
}
@Test
public void testDeleteSyntaxWithCompositeId() {
Session s = openSession();
Transaction t = s.beginTransaction();
s.createQuery( "delete EntityWithCrazyCompositeKey where id.id = 1 and id.otherId = 2" ).executeUpdate();
s.createQuery( "delete from EntityWithCrazyCompositeKey where id.id = 1 and id.otherId = 2" ).executeUpdate();
s.createQuery( "delete from EntityWithCrazyCompositeKey e where e.id.id = 1 and e.id.otherId = 2" ).executeUpdate();
t.commit();
s.close();
}
private class TestData {
private Animal polliwog;
private Animal catepillar;
private Animal frog;
private Animal butterfly;
private Zoo zoo;
private Zoo pettingZoo;
private void prepare() {
Session s = openSession();
Transaction txn = s.beginTransaction();
polliwog = new Animal();
polliwog.setBodyWeight( 12 );
polliwog.setDescription( "Polliwog" );
catepillar = new Animal();
catepillar.setBodyWeight( 10 );
catepillar.setDescription( "Catepillar" );
frog = new Animal();
frog.setBodyWeight( 34 );
frog.setDescription( "Frog" );
polliwog.setFather( frog );
frog.addOffspring( polliwog );
butterfly = new Animal();
butterfly.setBodyWeight( 9 );
butterfly.setDescription( "Butterfly" );
catepillar.setMother( butterfly );
butterfly.addOffspring( catepillar );
s.save( frog );
s.save( polliwog );
s.save( butterfly );
s.save( catepillar );
Dog dog = new Dog();
dog.setBodyWeight( 200 );
dog.setDescription( "dog" );
s.save( dog );
Cat cat = new Cat();
cat.setBodyWeight( 100 );
cat.setDescription( "cat" );
s.save( cat );
zoo = new Zoo();
zoo.setName( "Zoo" );
Address add = new Address();
add.setCity("MEL");
add.setCountry("AU");
add.setStreet("Main st");
add.setPostalCode("3000");
zoo.setAddress(add);
pettingZoo = new PettingZoo();
pettingZoo.setName( "Petting Zoo" );
Address addr = new Address();
addr.setCity("Sydney");
addr.setCountry("AU");
addr.setStreet("High st");
addr.setPostalCode("2000");
pettingZoo.setAddress(addr);
s.save( zoo );
s.save( pettingZoo );
Joiner joiner = new Joiner();
joiner.setJoinedName( "joined-name" );
joiner.setName( "name" );
s.save( joiner );
Car car = new Car();
car.setVin( "123c" );
car.setOwner( "Kirsten" );
s.save( car );
Truck truck = new Truck();
truck.setVin( "123t" );
truck.setOwner( "Steve" );
s.save( truck );
SUV suv = new SUV();
suv.setVin( "123s" );
suv.setOwner( "Joe" );
s.save( suv );
Pickup pickup = new Pickup();
pickup.setVin( "123p" );
pickup.setOwner( "Cecelia" );
s.save( pickup );
BooleanLiteralEntity bool = new BooleanLiteralEntity();
s.save( bool );
txn.commit();
s.close();
}
private void cleanup() {
Session s = openSession();
Transaction txn = s.beginTransaction();
// workaround awesome HSQLDB "feature"
s.createQuery( "delete from Animal where mother is not null or father is not null" ).executeUpdate();
s.createQuery( "delete from Animal" ).executeUpdate();
s.createQuery( "delete from Zoo" ).executeUpdate();
s.createQuery( "delete from Joiner" ).executeUpdate();
s.createQuery( "delete from Vehicle" ).executeUpdate();
s.createQuery( "delete from BooleanLiteralEntity" ).executeUpdate();
txn.commit();
s.close();
}
}
}