Merge remote-tracking branch 'upstream/master' into wip/6.0_merge_48

This commit is contained in:
Andrea Boriero 2020-07-24 07:17:21 +01:00
commit 476ffb4299
18 changed files with 1795 additions and 42 deletions

View File

@ -145,7 +145,7 @@ ext {
// EL required by Hibernate Validator at test runtime
expression_language: "org.glassfish:javax.el:${elVersion}",
c3p0: "com.mchange:c3p0:0.9.5.3",
c3p0: "com.mchange:c3p0:0.9.5.5",
ehcache: "net.sf.ehcache:ehcache:2.10.6",
ehcache3: "org.ehcache:ehcache:3.6.1",
jcache: "javax.cache:cache-api:1.0.0",

View File

@ -275,6 +275,9 @@ public class H2Dialect extends Dialect {
if ( idx > 0 ) {
return message.substring( idx + "violation: ".length() );
}
if ( sqle.getSQLState().equals("23506") ) {
return constraintName.substring( 1, constraintName.indexOf(":") );
}
}
return null;
} );

View File

@ -18,7 +18,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
@ -1832,7 +1831,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
// INSERTED KEYS HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private HashMap<String,List<Object>> insertedKeysMap;
private HashMap<String,HashSet<Object>> insertedKeysMap;
@Override
public void registerInsertedKey(EntityPersister persister, Object id) {
@ -1842,11 +1841,10 @@ public class StatefulPersistenceContext implements PersistenceContext {
insertedKeysMap = new HashMap<>();
}
final String rootEntityName = persister.getRootEntityName();
List<Object> insertedEntityIds = insertedKeysMap.get( rootEntityName );
if ( insertedEntityIds == null ) {
insertedEntityIds = new ArrayList<>();
insertedKeysMap.put( rootEntityName, insertedEntityIds );
}
HashSet<Object> insertedEntityIds = insertedKeysMap.computeIfAbsent(
rootEntityName,
k -> new HashSet<>()
);
insertedEntityIds.add( id );
}
}
@ -1856,7 +1854,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
// again, we only really care if the entity is cached
if ( persister.canWriteToCache() ) {
if ( insertedKeysMap != null ) {
final List<Object> insertedEntityIds = insertedKeysMap.get( persister.getRootEntityName() );
final HashSet<Object> insertedEntityIds = insertedKeysMap.get( persister.getRootEntityName() );
if ( insertedEntityIds != null ) {
return insertedEntityIds.contains( id );
}

View File

@ -15,6 +15,7 @@ import org.hibernate.engine.jdbc.batch.spi.BatchKey;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.resource.jdbc.spi.JdbcObserver;
import org.jboss.logging.Logger;
/**
@ -108,20 +109,21 @@ public class BatchingBatch extends AbstractBatchImpl {
private void performExecution() {
LOG.debugf( "Executing batch size: %s", batchPosition );
final JdbcObserver observer = getJdbcCoordinator().getJdbcSessionOwner().getJdbcSessionContext().getObserver();
try {
for ( Map.Entry<String,PreparedStatement> entry : getStatements().entrySet() ) {
String sql = entry.getKey();
final String sql = entry.getKey();
try {
final PreparedStatement statement = entry.getValue();
final int[] rowCounts;
try {
getJdbcCoordinator().getJdbcSessionOwner().getJdbcSessionContext().getObserver().jdbcExecuteBatchStart();
observer.jdbcExecuteBatchStart();
rowCounts = statement.executeBatch();
}
finally {
getJdbcCoordinator().getJdbcSessionOwner().getJdbcSessionContext().getObserver().jdbcExecuteBatchEnd();
observer.jdbcExecuteBatchEnd();
}
checkRowCounts( rowCounts, statement );
checkRowCounts( rowCounts, statement, sql );
}
catch ( SQLException e ) {
abortBatch();
@ -140,13 +142,13 @@ public class BatchingBatch extends AbstractBatchImpl {
}
}
private void checkRowCounts(int[] rowCounts, PreparedStatement ps) throws SQLException, HibernateException {
private void checkRowCounts(int[] rowCounts, PreparedStatement ps, String statementSQL) throws SQLException, HibernateException {
final int numberOfRowCounts = rowCounts.length;
if ( batchPosition != 0 && numberOfRowCounts != batchPosition / getStatements().size() ) {
LOG.unexpectedRowCounts();
}
for ( int i = 0; i < numberOfRowCounts; i++ ) {
getKey().getExpectation().verifyOutcome( rowCounts[i], ps, i );
getKey().getExpectation().verifyOutcome( rowCounts[i], ps, i, statementSQL );
}
}
}

View File

@ -40,16 +40,17 @@ public class NonBatchingBatch extends AbstractBatchImpl {
public void addToBatch() {
notifyObserversImplicitExecution();
for ( Map.Entry<String,PreparedStatement> entry : getStatements().entrySet() ) {
final String statementSQL = entry.getKey();
try {
final PreparedStatement statement = entry.getValue();
final int rowCount = jdbcCoordinator.getResultSetReturn().executeUpdate( statement );
getKey().getExpectation().verifyOutcome( rowCount, statement, 0 );
getKey().getExpectation().verifyOutcome( rowCount, statement, 0, statementSQL );
jdbcCoordinator.getResourceRegistry().release( statement );
jdbcCoordinator.afterStatementExecution();
}
catch ( SQLException e ) {
abortBatch();
throw sqlExceptionHelper().convert( e, "could not execute non-batched batch statement", entry.getKey() );
throw sqlExceptionHelper().convert( e, "could not execute non-batched batch statement", statementSQL );
}
catch (JDBCException e) {
abortBatch();

View File

@ -1189,9 +1189,11 @@ public class ActionQueue {
if ( nextBatchIdentifier.hasAnyParentEntityNames( batchIdentifier ) ) {
nextBatchIdentifier.parent = batchIdentifier;
nextBatchIdentifier.getParentEntityNames().add( batchIdentifier.getEntityName() );
}
if ( batchIdentifier.hasAnyChildEntityNames( nextBatchIdentifier ) ) {
nextBatchIdentifier.parent = batchIdentifier;
nextBatchIdentifier.getParentEntityNames().add( batchIdentifier.getEntityName() );
}
}
}

View File

@ -23,10 +23,11 @@ public interface Expectation {
* @param rowCount The RDBMS reported "number of rows affected".
* @param statement The statement representing the operation
* @param batchPosition The position in the batch (if batching)
* @param statementSQL The SQL backing the prepared statement, for logging purposes
* @throws SQLException Exception from the JDBC driver
* @throws HibernateException Problem processing the outcome.
*/
public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) throws SQLException, HibernateException;
public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String statementSQL) throws SQLException, HibernateException;
/**
* Perform any special statement preparation.

View File

@ -45,17 +45,17 @@ public class Expectations {
}
}
public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) {
public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String statementSQL) {
rowCount = determineRowCount( rowCount, statement );
if ( batchPosition < 0 ) {
checkNonBatched( rowCount, statement );
checkNonBatched( rowCount, statementSQL );
}
else {
checkBatched( rowCount, batchPosition, statement );
checkBatched( rowCount, batchPosition, statementSQL );
}
}
private void checkBatched(int rowCount, int batchPosition, PreparedStatement statement) {
private void checkBatched(int rowCount, int batchPosition, String statementSQL) {
if ( rowCount == -2 ) {
LOG.debugf( "Success of batch update unknown: %s", batchPosition );
}
@ -68,7 +68,7 @@ public class Expectations {
"Batch update returned unexpected row count from update ["
+ batchPosition + "]; actual row count: " + rowCount
+ "; expected: " + expectedRowCount + "; statement executed: "
+ statement
+ statementSQL
);
}
if ( expectedRowCount < rowCount ) {
@ -80,11 +80,11 @@ public class Expectations {
}
}
private void checkNonBatched(int rowCount, PreparedStatement statement) {
private void checkNonBatched(int rowCount, String statementSQL) {
if ( expectedRowCount > rowCount ) {
throw new StaleStateException(
"Unexpected row count: " + rowCount + "; expected: " + expectedRowCount
+ "; statement executed: " + statement
+ "; statement executed: " + statementSQL
);
}
if ( expectedRowCount < rowCount ) {
@ -150,7 +150,7 @@ public class Expectations {
// Various Expectation instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public static final Expectation NONE = new Expectation() {
public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) {
public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String statementSQL) {
// explicitly doAfterTransactionCompletion no checking...
}

View File

@ -1335,7 +1335,7 @@ public abstract class AbstractCollectionPersister
Expectation expectation = Expectations.appropriateExpectation( getDeleteAllCheckStyle() );
boolean callable = isDeleteAllCallable();
boolean useBatch = expectation.canBeBatched();
String sql = getSQLDeleteString();
final String sql = getSQLDeleteString();
if ( useBatch ) {
if ( removeBatchKey == null ) {
removeBatchKey = new BasicBatchKey(
@ -1366,7 +1366,7 @@ public abstract class AbstractCollectionPersister
.addToBatch();
}
else {
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1 );
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1, sql );
}
}
catch ( SQLException sqle ) {
@ -1436,7 +1436,7 @@ public abstract class AbstractCollectionPersister
final PreparedStatement st;
boolean callable = isInsertCallable();
boolean useBatch = expectation.canBeBatched();
String sql = getSQLInsertRowString();
final String sql = getSQLInsertRowString();
if ( useBatch ) {
if ( recreateBatchKey == null ) {
@ -1475,7 +1475,7 @@ public abstract class AbstractCollectionPersister
}
else {
expectation.verifyOutcome( jdbcCoordinator
.getResultSetReturn().executeUpdate( st ), st, -1 );
.getResultSetReturn().executeUpdate( st ), st, -1, sql );
}
collection.afterRowInsert( this, entry, i );
@ -1552,7 +1552,7 @@ public abstract class AbstractCollectionPersister
final PreparedStatement st;
boolean callable = isDeleteCallable();
boolean useBatch = expectation.canBeBatched();
String sql = getSQLDeleteRowString();
final String sql = getSQLDeleteRowString();
if ( useBatch ) {
if ( deleteBatchKey == null ) {
@ -1598,7 +1598,7 @@ public abstract class AbstractCollectionPersister
.addToBatch();
}
else {
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1 );
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1, sql );
}
count++;
}
@ -1710,7 +1710,7 @@ public abstract class AbstractCollectionPersister
session.getJdbcCoordinator().getBatch( insertBatchKey ).addToBatch();
}
else {
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1 );
expectation.verifyOutcome( session.getJdbcCoordinator().getResultSetReturn().executeUpdate( st ), st, -1, sql );
}
collection.afterRowInsert( this, entry, i );
count++;

View File

@ -304,7 +304,7 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
expectation.verifyOutcome(
session.getJdbcCoordinator().getResultSetReturn().executeUpdate(
st
), st, -1
), st, -1, sql
);
}
}

View File

@ -264,7 +264,7 @@ public class OneToManyPersister extends AbstractCollectionPersister {
expectation.verifyOutcome(
session.getJdbcCoordinator()
.getResultSetReturn()
.executeUpdate( st ), st, -1
.executeUpdate( st ), st, -1, sql
);
}
}
@ -373,7 +373,7 @@ public class OneToManyPersister extends AbstractCollectionPersister {
deleteExpectation.verifyOutcome(
session.getJdbcCoordinator()
.getResultSetReturn()
.executeUpdate( st ), st, -1
.executeUpdate( st ), st, -1, sql
);
}
count++;
@ -445,7 +445,7 @@ public class OneToManyPersister extends AbstractCollectionPersister {
insertExpectation.verifyOutcome(
session.getJdbcCoordinator()
.getResultSetReturn()
.executeUpdate( st ), st, -1
.executeUpdate( st ), st, -1, sql
);
}
count++;

View File

@ -2736,9 +2736,10 @@ public abstract class AbstractEntityPersister
Object id,
int tableNumber,
Expectation expectation,
PreparedStatement statement) throws HibernateException {
PreparedStatement statement,
String statementSQL) throws HibernateException {
try {
expectation.verifyOutcome( rows, statement, -1 );
expectation.verifyOutcome( rows, statement, -1, statementSQL );
}
catch (StaleStateException e) {
if ( !isNullableTable( tableNumber ) ) {
@ -3396,7 +3397,7 @@ public abstract class AbstractEntityPersister
expectation.verifyOutcome(
session.getJdbcCoordinator()
.getResultSetReturn()
.executeUpdate( insert ), insert, -1
.executeUpdate( insert ), insert, -1, sql
);
}
}
@ -3595,7 +3596,8 @@ public abstract class AbstractEntityPersister
id,
j,
expectation,
update
update,
sql
);
}
@ -3720,7 +3722,8 @@ public abstract class AbstractEntityPersister
id,
j,
expectation,
delete
delete,
sql
);
}

View File

@ -0,0 +1,228 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.engine.spi;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jta.TestingJtaBootstrap;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
/*
* @author gajendra.jatav(raaz2.gajendra@gmail.com)
*/
public class BatchSortingTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void addSettings(Map settings) {
settings.put( Environment.ORDER_INSERTS, "true" );
settings.put( Environment.ORDER_UPDATES, "true" );
settings.put( Environment.STATEMENT_BATCH_SIZE, "5" );
TestingJtaBootstrap.prepare( settings );
}
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{
GeoCountry.class, GeoDistrict.class,
GeoDistrictDetail.class, GeoNation.class
};
}
@Test
@TestForIssue( jiraKey = "HHH-13410" )
public void batchInsertTest() {
doInHibernate(this::sessionFactory, session -> {
GeoCountry country = new GeoCountry();
GeoDistrict geoDistrict = new GeoDistrict();
geoDistrict.setDistrictName( "SomeDistrict" );
GeoDistrictDetail districtDetail = new GeoDistrictDetail();
geoDistrict.setGeoDistrictDetail( districtDetail );
GeoNation nation = new GeoNation();
nation.setNationName( "NationName" );
country.setCountryName( "CountryName" );
session.persist( country );
List<GeoDistrict> geoDistricts = new ArrayList<>();
geoDistrict.setCountryId( country.getId() );
geoDistricts.add( geoDistrict );
country.setDistricts( geoDistricts );
session.persist( geoDistrict );
session.persist( nation );
});
}
@Entity
public static class GeoCountry {
@GeneratedValue
@Id
private Long id;
@OneToMany( fetch = FetchType.LAZY)
@JoinColumn( name = "COUNTRY_ID", referencedColumnName = "ID", updatable = false, insertable = false)
private List<GeoDistrict> districts;
@OneToOne( fetch = FetchType.LAZY)
@JoinColumn( name = "NATION_ID")
private GeoNation nation;
private String countryName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<GeoDistrict> getDistricts() {
return districts;
}
public void setDistricts(List<GeoDistrict> districts) {
this.districts = districts;
}
public GeoNation getNation() {
return nation;
}
public void setNation(GeoNation nation) {
this.nation = nation;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
@Entity
public static class GeoDistrict {
@GeneratedValue
@Id
private Long id;
private String districtName;
@Column( name = "COUNTRY_ID")
private Long countryId;
@OneToOne( fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinColumn( name = "DISTRICT_DTL_ID")
private GeoDistrictDetail geoDistrictDetail;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDistrictName() {
return districtName;
}
public void setDistrictName(String districtName) {
this.districtName = districtName;
}
public Long getCountryId() {
return countryId;
}
public void setCountryId(Long countryId) {
this.countryId = countryId;
}
public GeoDistrictDetail getGeoDistrictDetail() {
return geoDistrictDetail;
}
public void setGeoDistrictDetail(GeoDistrictDetail geoDistrictDetail) {
this.geoDistrictDetail = geoDistrictDetail;
}
}
@Entity
public static class GeoDistrictDetail {
@GeneratedValue
@Id
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
@Entity
public static class GeoNation {
@GeneratedValue
@Id
private Long id;
@Column( name = "GOV_ID")
private Long govId;
@Column( name = "TEAM_ID")
private Long teamId;
private String nationName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getGovId() {
return govId;
}
public void setGovId(Long govId) {
this.govId = govId;
}
public Long getTeamId() {
return teamId;
}
public void setTeamId(Long teamId) {
this.teamId = teamId;
}
public String getNationName() {
return nationName;
}
public void setNationName(String nationName) {
this.nationName = nationName;
}
}
}

View File

@ -92,6 +92,7 @@ public class BatchOptimisticLockingTest extends
}
catch (Exception expected) {
assertEquals( OptimisticLockException.class, expected.getClass());
assertEquals( "Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1; statement executed: update Person set name=?, version=? where id=? and version=?", expected.getMessage() );
}
}

View File

@ -0,0 +1,380 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.inheritance;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@TestForIssue(jiraKey = "HHH-14103")
public class TransientOverrideAsPersistentJoined extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void testFindByRootClass() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.find( Employee.class, "Jane Smith" );
assertNotNull( editor );
assertEquals( "Senior Editor", editor.getTitle() );
final Employee writer = session.find( Employee.class, "John Smith" );
assertTrue( Writer.class.isInstance( writer ) );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( ( (Writer) writer ).getGroup() );
final Group group = ( (Writer) writer ).getGroup();
assertEquals( writer.getTitle(), group.getName() );
final Job jobEditor = session.find( Job.class, "Edit" );
assertSame( editor, jobEditor.getEmployee() );
final Job jobWriter = session.find( Job.class, "Write" );
assertSame( writer, jobWriter.getEmployee() );
});
}
@Test
public void testFindBySubclass() {
doInHibernate( this::sessionFactory, session -> {
final Editor editor = session.find( Editor.class, "Jane Smith" );
assertNotNull( editor );
assertEquals( "Senior Editor", editor.getTitle() );
final Writer writer = session.find( Writer.class, "John Smith" );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( writer.getGroup() );
final Group group = writer.getGroup();
assertEquals( writer.getTitle(), group.getName() );
final Job jobEditor = session.find( Job.class, "Edit" );
assertSame( editor, jobEditor.getEmployee() );
final Job jobWriter = session.find( Job.class, "Write" );
assertSame( writer, jobWriter.getEmployee() );
});
}
@Test
public void testQueryByRootClass() {
doInHibernate( this::sessionFactory, session -> {
final List<Employee> employees = session.createQuery( "from Employee", Employee.class )
.getResultList();
assertEquals( 2, employees.size() );
assertTrue( Editor.class.isInstance( employees.get( 0 ) ) );
assertTrue( Writer.class.isInstance( employees.get( 1 ) ) );
final Editor editor = (Editor) employees.get( 0 );
assertEquals( "Senior Editor", editor.getTitle() );
final Writer writer = (Writer) employees.get( 1 );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( writer.getGroup() );
final Group group = writer.getGroup();
assertEquals( writer.getTitle(), group.getName() );
});
}
@Test
@FailureExpected( jiraKey = "HHH-12981")
public void testQueryByRootClassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.createQuery( "from Employee where title=:title", Employee.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Employee writer = session.createQuery( "from Employee where title=:title", Employee.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Test
@FailureExpected( jiraKey = "HHH-12981")
public void testQueryByRootClassAndOverridenPropertyTreat() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.createQuery( "from Employee e where treat( e as Editor ).title=:title", Employee.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Employee writer = session.createQuery( "from Employee e where treat( e as Writer).title=:title", Employee.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Test
public void testQueryBySublassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final Editor editor = session.createQuery( "from Editor where title=:title", Editor.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Writer writer = session.createQuery( "from Writer where title=:title", Writer.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertNotNull( writer.getGroup() );
assertEquals( writer.getTitle(), writer.getGroup().getName() );
});
}
@Test
@FailureExpected( jiraKey = "HHH-12981")
public void testCriteriaQueryByRootClassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final CriteriaBuilder builder = session.getCriteriaBuilder();
final CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
final Root<Employee> root = query.from( Employee.class );
final ParameterExpression<String> parameter = builder.parameter( String.class, "title" );
final Predicate predicateEditor = builder.equal(
builder.treat( root, Editor.class ).get( "title" ),
parameter
);
query.where( predicateEditor );
final Employee editor = session.createQuery( query )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Predicate predicateWriter = builder.equal(
builder.treat( root, Writer.class ).get( "title" ),
parameter
);
query.where( predicateWriter );
final Employee writer = session.createQuery( query )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Before
public void setupData() {
doInHibernate( this::sessionFactory, session -> {
Job jobEditor = new Job("Edit");
jobEditor.setEmployee(new Editor("Jane Smith", "Senior Editor"));
Job jobWriter= new Job("Write");
jobWriter.setEmployee(new Writer("John Smith", new Group("Writing")));
Employee editor = jobEditor.getEmployee();
Employee writer = jobWriter.getEmployee();
Group group = Writer.class.cast( writer ).getGroup();
session.persist( editor );
session.persist( group );
session.persist( writer );
session.persist( jobEditor );
session.persist( jobWriter );
});
}
@After
public void cleanupData() {
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "delete from Job" ).executeUpdate();
session.createQuery( "delete from Employee" ).executeUpdate();
session.createQuery( "delete from Group" ).executeUpdate();
});
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Employee.class,
Editor.class,
Writer.class,
Group.class,
Job.class
};
}
@Entity(name="Employee")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name="department")
public static abstract class Employee {
private String name;
private String title;
protected Employee(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
@Transient
public String getTitle() {
return title;
}
protected Employee() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
protected void setTitle(String title) {
this.title = title;
}
}
@Entity(name = "Editor")
public static class Editor extends Employee {
public Editor(String name, String title) {
super(name);
setTitle( title );
}
@Column(name = "e_title")
public String getTitle() {
return super.getTitle();
}
public void setTitle(String title) {
super.setTitle( title );
}
protected Editor() {
// this form used by Hibernate
super();
}
}
@Entity(name = "Writer")
public static class Writer extends Employee {
private Group group;
public Writer(String name, Group group) {
super(name);
setGroup(group);
}
// Cannot have a constraint on e_title because
// Editor#title (which uses the same e_title column) can be non-null,
// and there is no associated group.
@ManyToOne(optional = false)
@JoinColumn(name = "e_title", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
public Group getGroup() {
return group;
}
@Column(name = "e_title", insertable = false, updatable = false)
public String getTitle() {
return super.getTitle();
}
public void setTitle(String title) {
super.setTitle( title );
}
protected Writer() {
// this form used by Hibernate
super();
}
protected void setGroup(Group group) {
this.group = group;
setTitle( group.getName() );
}
}
@Entity(name = "Group")
@Table(name = "WorkGroup")
public static class Group {
private String name;
public Group(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
protected Group() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
}
@Entity(name = "Job")
public static class Job {
private String name;
private Employee employee;
public Job(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
@OneToOne
@JoinColumn(name="employee_name")
public Employee getEmployee() {
return employee;
}
protected Job() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
protected void setEmployee(Employee e) {
this.employee = e;
}
}
}

View File

@ -0,0 +1,381 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.inheritance;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@TestForIssue(jiraKey = "HHH-14103")
public class TransientOverrideAsPersistentMappedSuperclass extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void testFindByRootClass() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.find( Employee.class, "Jane Smith" );
assertNotNull( editor );
assertEquals( "Senior Editor", editor.getTitle() );
final Employee writer = session.find( Employee.class, "John Smith" );
assertTrue( Writer.class.isInstance( writer ) );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( ( (Writer) writer ).getGroup() );
final Group group = ( (Writer) writer ).getGroup();
assertEquals( writer.getTitle(), group.getName() );
final Job jobEditor = session.find( Job.class, "Edit" );
assertSame( editor, jobEditor.getEmployee() );
final Job jobWriter = session.find( Job.class, "Write" );
assertSame( writer, jobWriter.getEmployee() );
});
}
@Test
public void testFindBySubclass() {
doInHibernate( this::sessionFactory, session -> {
final Editor editor = session.find( Editor.class, "Jane Smith" );
assertNotNull( editor );
assertEquals( "Senior Editor", editor.getTitle() );
final Writer writer = session.find( Writer.class, "John Smith" );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( writer.getGroup() );
final Group group = writer.getGroup();
assertEquals( writer.getTitle(), group.getName() );
final Job jobEditor = session.find( Job.class, "Edit" );
assertSame( editor, jobEditor.getEmployee() );
final Job jobWriter = session.find( Job.class, "Write" );
assertSame( writer, jobWriter.getEmployee() );
});
}
@Test
public void testQueryByRootClass() {
doInHibernate( this::sessionFactory, session -> {
final List<Employee> employees = session.createQuery( "from Employee", Employee.class )
.getResultList();
assertEquals( 2, employees.size() );
assertTrue( Editor.class.isInstance( employees.get( 0 ) ) );
assertTrue( Writer.class.isInstance( employees.get( 1 ) ) );
final Editor editor = (Editor) employees.get( 0 );
assertEquals( "Senior Editor", editor.getTitle() );
final Writer writer = (Writer) employees.get( 1 );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( writer.getGroup() );
final Group group = writer.getGroup();
assertEquals( writer.getTitle(), group.getName() );
});
}
@Test
public void testQueryByRootClassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.createQuery( "from Employee where title=:title", Employee.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Employee writer = session.createQuery( "from Employee where title=:title", Employee.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Test
public void testQueryByRootClassAndOverridenPropertyTreat() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.createQuery( "from Employee e where treat( e as Editor ).title=:title", Employee.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Employee writer = session.createQuery( "from Employee e where treat( e as Writer).title=:title", Employee.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Test
public void testQueryBySublassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final Editor editor = session.createQuery( "from Editor where title=:title", Editor.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Writer writer = session.createQuery( "from Writer where title=:title", Writer.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertNotNull( writer.getGroup() );
assertEquals( writer.getTitle(), writer.getGroup().getName() );
});
}
@Test
public void testCriteriaQueryByRootClassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final CriteriaBuilder builder = session.getCriteriaBuilder();
final CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
final Root<Employee> root = query.from( Employee.class );
final ParameterExpression<String> parameter = builder.parameter( String.class, "title" );
final Predicate predicateEditor = builder.equal(
builder.treat( root, Editor.class ).get( "title" ),
parameter
);
query.where( predicateEditor );
final Employee editor = session.createQuery( query )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Predicate predicateWriter = builder.equal(
builder.treat( root, Writer.class ).get( "title" ),
parameter
);
query.where( predicateWriter );
final Employee writer = session.createQuery( query )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Before
public void setupData() {
doInHibernate( this::sessionFactory, session -> {
Job jobEditor = new Job("Edit");
jobEditor.setEmployee(new Editor("Jane Smith", "Senior Editor"));
Job jobWriter= new Job("Write");
jobWriter.setEmployee(new Writer("John Smith", new Group("Writing")));
Employee editor = jobEditor.getEmployee();
Employee writer = jobWriter.getEmployee();
Group group = Writer.class.cast( writer ).getGroup();
session.persist( editor );
session.persist( group );
session.persist( writer );
session.persist( jobEditor );
session.persist( jobWriter );
});
}
@After
public void cleanupData() {
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "delete from Job" ).executeUpdate();
session.createQuery( "delete from Employee" ).executeUpdate();
session.createQuery( "delete from Group" ).executeUpdate();
});
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Employee.class,
Editor.class,
Writer.class,
Group.class,
Job.class
};
}
@MappedSuperclass
public static class AbstractEmployee {
private String title;
@Transient
public String getTitle() {
return title;
}
protected void setTitle(String title) {
this.title = title;
}
}
@Entity(name="Employee")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="department")
public static abstract class Employee extends AbstractEmployee {
private String name;
protected Employee(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
protected Employee() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
}
@Entity(name = "Editor")
public static class Editor extends Employee {
public Editor(String name, String title) {
super(name);
setTitle( title );
}
@Column(name = "e_title")
public String getTitle() {
return super.getTitle();
}
public void setTitle(String title) {
super.setTitle( title );
}
protected Editor() {
// this form used by Hibernate
super();
}
}
@Entity(name = "Writer")
public static class Writer extends Employee {
private Group group;
public Writer(String name, Group group) {
super(name);
setGroup(group);
}
// Cannot have a constraint on e_title because
// Editor#title (which uses the same e_title column) can be non-null,
// and there is no associated group.
@ManyToOne(optional = false)
@JoinColumn(name = "e_title", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
public Group getGroup() {
return group;
}
@Column(name = "e_title", insertable = false, updatable = false)
public String getTitle() {
return super.getTitle();
}
public void setTitle(String title) {
super.setTitle( title );
}
protected Writer() {
// this form used by Hibernate
super();
}
protected void setGroup(Group group) {
this.group = group;
setTitle( group.getName() );
}
}
@Entity(name = "Group")
@Table(name = "WorkGroup")
public static class Group {
private String name;
public Group(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
protected Group() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
}
@Entity(name = "Job")
public static class Job {
private String name;
private Employee employee;
public Job(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
@OneToOne
@JoinColumn(name="employee_name")
public Employee getEmployee() {
return employee;
}
protected Job() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
protected void setEmployee(Employee e) {
this.employee = e;
}
}
}

View File

@ -0,0 +1,377 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.inheritance;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@TestForIssue(jiraKey = "HHH-14103")
public class TransientOverrideAsPersistentSingleTable extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void testFindByRootClass() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.find( Employee.class, "Jane Smith" );
assertNotNull( editor );
assertEquals( "Senior Editor", editor.getTitle() );
final Employee writer = session.find( Employee.class, "John Smith" );
assertTrue( Writer.class.isInstance( writer ) );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( ( (Writer) writer ).getGroup() );
final Group group = ( (Writer) writer ).getGroup();
assertEquals( writer.getTitle(), group.getName() );
final Job jobEditor = session.find( Job.class, "Edit" );
assertSame( editor, jobEditor.getEmployee() );
final Job jobWriter = session.find( Job.class, "Write" );
assertSame( writer, jobWriter.getEmployee() );
});
}
@Test
public void testFindBySubclass() {
doInHibernate( this::sessionFactory, session -> {
final Editor editor = session.find( Editor.class, "Jane Smith" );
assertNotNull( editor );
assertEquals( "Senior Editor", editor.getTitle() );
final Writer writer = session.find( Writer.class, "John Smith" );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( writer.getGroup() );
final Group group = writer.getGroup();
assertEquals( writer.getTitle(), group.getName() );
final Job jobEditor = session.find( Job.class, "Edit" );
assertSame( editor, jobEditor.getEmployee() );
final Job jobWriter = session.find( Job.class, "Write" );
assertSame( writer, jobWriter.getEmployee() );
});
}
@Test
public void testQueryByRootClass() {
doInHibernate( this::sessionFactory, session -> {
final List<Employee> employees = session.createQuery( "from Employee", Employee.class )
.getResultList();
assertEquals( 2, employees.size() );
assertTrue( Editor.class.isInstance( employees.get( 0 ) ) );
assertTrue( Writer.class.isInstance( employees.get( 1 ) ) );
final Editor editor = (Editor) employees.get( 0 );
assertEquals( "Senior Editor", editor.getTitle() );
final Writer writer = (Writer) employees.get( 1 );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( writer.getGroup() );
final Group group = writer.getGroup();
assertEquals( writer.getTitle(), group.getName() );
});
}
@Test
public void testQueryByRootClassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.createQuery( "from Employee where title=:title", Employee.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Employee writer = session.createQuery( "from Employee where title=:title", Employee.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Test
public void testQueryByRootClassAndOverridenPropertyTreat() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.createQuery( "from Employee e where treat( e as Editor ).title=:title", Employee.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Employee writer = session.createQuery( "from Employee e where treat( e as Writer).title=:title", Employee.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Test
public void testQueryBySublassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final Editor editor = session.createQuery( "from Editor where title=:title", Editor.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Writer writer = session.createQuery( "from Writer where title=:title", Writer.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertNotNull( writer.getGroup() );
assertEquals( writer.getTitle(), writer.getGroup().getName() );
});
}
@Test
public void testCriteriaQueryByRootClassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final CriteriaBuilder builder = session.getCriteriaBuilder();
final CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
final Root<Employee> root = query.from( Employee.class );
final ParameterExpression<String> parameter = builder.parameter( String.class, "title" );
final Predicate predicateEditor = builder.equal(
builder.treat( root, Editor.class ).get( "title" ),
parameter
);
query.where( predicateEditor );
final Employee editor = session.createQuery( query )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Predicate predicateWriter = builder.equal(
builder.treat( root, Writer.class ).get( "title" ),
parameter
);
query.where( predicateWriter );
final Employee writer = session.createQuery( query )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Before
public void setupData() {
doInHibernate( this::sessionFactory, session -> {
Job jobEditor = new Job("Edit");
jobEditor.setEmployee(new Editor("Jane Smith", "Senior Editor"));
Job jobWriter= new Job("Write");
jobWriter.setEmployee(new Writer("John Smith", new Group("Writing")));
Employee editor = jobEditor.getEmployee();
Employee writer = jobWriter.getEmployee();
Group group = Writer.class.cast( writer ).getGroup();
session.persist( editor );
session.persist( group );
session.persist( writer );
session.persist( jobEditor );
session.persist( jobWriter );
});
}
@After
public void cleanupData() {
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "delete from Job" ).executeUpdate();
session.createQuery( "delete from Employee" ).executeUpdate();
session.createQuery( "delete from Group" ).executeUpdate();
});
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Employee.class,
Editor.class,
Writer.class,
Group.class,
Job.class
};
}
@Entity(name="Employee")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="department")
public static abstract class Employee {
private String name;
private String title;
protected Employee(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
@Transient
public String getTitle() {
return title;
}
protected Employee() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
protected void setTitle(String title) {
this.title = title;
}
}
@Entity(name = "Editor")
public static class Editor extends Employee {
public Editor(String name, String title) {
super(name);
setTitle( title );
}
@Column(name = "e_title")
public String getTitle() {
return super.getTitle();
}
public void setTitle(String title) {
super.setTitle( title );
}
protected Editor() {
// this form used by Hibernate
super();
}
}
@Entity(name = "Writer")
public static class Writer extends Employee {
private Group group;
public Writer(String name, Group group) {
super(name);
setGroup(group);
}
// Cannot have a constraint on e_title because
// Editor#title (which uses the same e_title column) can be non-null,
// and there is no associated group.
@ManyToOne(optional = false)
@JoinColumn(name = "e_title", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
public Group getGroup() {
return group;
}
@Column(name = "e_title", insertable = false, updatable = false)
public String getTitle() {
return super.getTitle();
}
public void setTitle(String title) {
super.setTitle( title );
}
protected Writer() {
// this form used by Hibernate
super();
}
protected void setGroup(Group group) {
this.group = group;
setTitle( group.getName() );
}
}
@Entity(name = "Group")
@Table(name = "WorkGroup")
public static class Group {
private String name;
public Group(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
protected Group() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
}
@Entity(name = "Job")
public static class Job {
private String name;
private Employee employee;
public Job(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
@OneToOne
@JoinColumn(name="employee_name")
public Employee getEmployee() {
return employee;
}
protected Job() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
protected void setEmployee(Employee e) {
this.employee = e;
}
}
}

View File

@ -0,0 +1,376 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.inheritance;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@TestForIssue(jiraKey = "HHH-14103")
public class TransientOverrideAsPersistentTablePerClass extends BaseNonConfigCoreFunctionalTestCase {
@Test
public void testFindByRootClass() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.find( Employee.class, "Jane Smith" );
assertNotNull( editor );
assertEquals( "Senior Editor", editor.getTitle() );
final Employee writer = session.find( Employee.class, "John Smith" );
assertTrue( Writer.class.isInstance( writer ) );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( ( (Writer) writer ).getGroup() );
final Group group = ( (Writer) writer ).getGroup();
assertEquals( writer.getTitle(), group.getName() );
final Job jobEditor = session.find( Job.class, "Edit" );
assertSame( editor, jobEditor.getEmployee() );
final Job jobWriter = session.find( Job.class, "Write" );
assertSame( writer, jobWriter.getEmployee() );
});
}
@Test
public void testFindBySubclass() {
doInHibernate( this::sessionFactory, session -> {
final Editor editor = session.find( Editor.class, "Jane Smith" );
assertNotNull( editor );
assertEquals( "Senior Editor", editor.getTitle() );
final Writer writer = session.find( Writer.class, "John Smith" );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( writer.getGroup() );
final Group group = writer.getGroup();
assertEquals( writer.getTitle(), group.getName() );
final Job jobEditor = session.find( Job.class, "Edit" );
assertSame( editor, jobEditor.getEmployee() );
final Job jobWriter = session.find( Job.class, "Write" );
assertSame( writer, jobWriter.getEmployee() );
});
}
@Test
public void testQueryByRootClass() {
doInHibernate( this::sessionFactory, session -> {
final List<Employee> employees = session.createQuery( "from Employee", Employee.class )
.getResultList();
assertEquals( 2, employees.size() );
assertTrue( Editor.class.isInstance( employees.get( 0 ) ) );
assertTrue( Writer.class.isInstance( employees.get( 1 ) ) );
final Editor editor = (Editor) employees.get( 0 );
assertEquals( "Senior Editor", editor.getTitle() );
final Writer writer = (Writer) employees.get( 1 );
assertEquals( "Writing", writer.getTitle() );
assertNotNull( writer.getGroup() );
final Group group = writer.getGroup();
assertEquals( writer.getTitle(), group.getName() );
});
}
@Test
public void testQueryByRootClassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.createQuery( "from Employee where title=:title", Employee.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Employee writer = session.createQuery( "from Employee where title=:title", Employee.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Test
public void testQueryByRootClassAndOverridenPropertyTreat() {
doInHibernate( this::sessionFactory, session -> {
final Employee editor = session.createQuery( "from Employee e where treat( e as Editor ).title=:title", Employee.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Employee writer = session.createQuery( "from Employee e where treat( e as Writer).title=:title", Employee.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Test
public void testQueryBySublassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final Editor editor = session.createQuery( "from Editor where title=:title", Editor.class )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Writer writer = session.createQuery( "from Writer where title=:title", Writer.class )
.setParameter( "title", "Writing" )
.getSingleResult();
assertNotNull( writer.getGroup() );
assertEquals( writer.getTitle(), writer.getGroup().getName() );
});
}
@Test
public void testCriteriaQueryByRootClassAndOverridenProperty() {
doInHibernate( this::sessionFactory, session -> {
final CriteriaBuilder builder = session.getCriteriaBuilder();
final CriteriaQuery<Employee> query = builder.createQuery( Employee.class );
final Root<Employee> root = query.from( Employee.class );
final ParameterExpression<String> parameter = builder.parameter( String.class, "title" );
final Predicate predicateEditor = builder.equal(
builder.treat( root, Editor.class ).get( "title" ),
parameter
);
query.where( predicateEditor );
final Employee editor = session.createQuery( query )
.setParameter( "title", "Senior Editor" )
.getSingleResult();
assertTrue( Editor.class.isInstance( editor ) );
final Predicate predicateWriter = builder.equal(
builder.treat( root, Writer.class ).get( "title" ),
parameter
);
query.where( predicateWriter );
final Employee writer = session.createQuery( query )
.setParameter( "title", "Writing" )
.getSingleResult();
assertTrue( Writer.class.isInstance( writer ) );
assertNotNull( ( (Writer) writer ).getGroup() );
assertEquals( writer.getTitle(), ( (Writer) writer ).getGroup() .getName() );
});
}
@Before
public void setupData() {
doInHibernate( this::sessionFactory, session -> {
Job jobEditor = new Job("Edit");
jobEditor.setEmployee(new Editor("Jane Smith", "Senior Editor"));
Job jobWriter= new Job("Write");
jobWriter.setEmployee(new Writer("John Smith", new Group("Writing")));
Employee editor = jobEditor.getEmployee();
Employee writer = jobWriter.getEmployee();
Group group = Writer.class.cast( writer ).getGroup();
session.persist( editor );
session.persist( group );
session.persist( writer );
session.persist( jobEditor );
session.persist( jobWriter );
});
}
@After
public void cleanupData() {
doInHibernate( this::sessionFactory, session -> {
session.createQuery( "delete from Job" ).executeUpdate();
session.createQuery( "delete from Employee" ).executeUpdate();
session.createQuery( "delete from Group" ).executeUpdate();
});
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Employee.class,
Editor.class,
Writer.class,
Group.class,
Job.class
};
}
@Entity(name="Employee")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name="department")
public static abstract class Employee {
private String name;
private String title;
protected Employee(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
@Transient
public String getTitle() {
return title;
}
protected Employee() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
protected void setTitle(String title) {
this.title = title;
}
}
@Entity(name = "Editor")
public static class Editor extends Employee {
public Editor(String name, String title) {
super(name);
setTitle( title );
}
@Column(name = "e_title")
public String getTitle() {
return super.getTitle();
}
public void setTitle(String title) {
super.setTitle( title );
}
protected Editor() {
// this form used by Hibernate
super();
}
}
@Entity(name = "Writer")
public static class Writer extends Employee {
private Group group;
public Writer(String name, Group group) {
super(name);
setGroup(group);
}
// Cannot have a constraint on e_title because
// Editor#title (which uses the same e_title column) can be non-null,
// and there is no associated group.
@ManyToOne(optional = false)
@JoinColumn(name = "e_title", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
public Group getGroup() {
return group;
}
@Column(name = "e_title", insertable = false, updatable = false)
public String getTitle() {
return super.getTitle();
}
public void setTitle(String title) {
super.setTitle( title );
}
protected Writer() {
// this form used by Hibernate
super();
}
protected void setGroup(Group group) {
this.group = group;
setTitle( group.getName() );
}
}
@Entity(name = "Group")
@Table(name = "WorkGroup")
public static class Group {
private String name;
public Group(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
protected Group() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
}
@Entity(name = "Job")
public static class Job {
private String name;
private Employee employee;
public Job(String name) {
this();
setName(name);
}
@Id
public String getName() {
return name;
}
@OneToOne
@JoinColumn(name="employee_name")
public Employee getEmployee() {
return employee;
}
protected Job() {
// this form used by Hibernate
}
protected void setName(String name) {
this.name = name;
}
protected void setEmployee(Employee e) {
this.employee = e;
}
}
}