Hibernate extends an object for a single table

I have a table with two fields. I would like to have two objects.

The first has a field1

@Entity(name = "simpleTableObject") @Table(name = "someTable") public class SimpleTableObject { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") protected long id; @Column(name = "field1") private String field1; 

The second has all two fields

 @Entity(name = "tableObject") @Table(name = "someTable") public class TableObject { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") protected long id; @Column(name = "field1") private String field1; @Column(name = "field2") private String field2; 

I load everyone using

 @Transactional(readOnly = true) @SuppressWarnings("unchecked") public List get(Class aClass) { ClassMetadata hibernateMetadata = sessionFactory.getClassMetadata(aClass); if (hibernateMetadata == null) { return null; } if (hibernateMetadata instanceof AbstractEntityPersister) { AbstractEntityPersister persister = (AbstractEntityPersister) hibernateMetadata; String tableName = persister.getTableName(); if (tableName != null) { return sessionFactory.getCurrentSession(). createQuery("from " + tableName).list(); } } return null; } 

I would like to make TableObject extend SimpleTableObject . How can I do it?

+8
java hibernate
source share
5 answers

If you want to save common fields in your required tables, then you have classes A and B, and you have some common files, such as created_by, updated_by, and you want to save field1, field2 in both objects: IN at the base level data:

 query> select * from A; +----++------------------------+ | id | created_by | updated_by | +----+------------+------------+ | 3 | xyz | abc | +----+------------+------------+ query> select * from B; +----++------------------------+ | id | created_by | updated_by | +----+------------+------------+ | 3 | xyz | abc | +----+------------+------------+ 

for this type of structure you should use @MappedSuperclass as suggested by @Dragan Bozanovic

But if you want the parent-child relationship and want to create a table for each class, you can use @Inheritance (strategy = InheritanceType.TABLE_PER_CLASS) it will create a table for each class For example: suppose you have 2 classes Payment and CreditCard, payment is parent class for CreditCard.

 @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class Payment { @Id @GeneratedValue(strategy = GenerationType.TABLE) private int id; @Column(nullable = false) private double amount; public int getId() { return id; } public void setId(int id) { this.id = id; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } } @Entity public class CreditCard extends Payment { private String ccNumber; private Date expireDate; public String getCcNumber() { return ccNumber; } public void setCcNumber(String ccNumber) { this.ccNumber = ccNumber; } public Date getExpireDate() { return expireDate; } public void setExpireDate(Date expireDate) { this.expireDate = expireDate; } } 

Now you save the date:

 public class TestConcreteClasses { public static void main(String[] args) { Payment payment = new Payment(); payment.setAmount(52.6); createData(payment); CreditCard creditCard = new CreditCard(); creditCard.setAmount(10); creditCard.setCcNumber("2536985474561236"); creditCard.setExpireDate(new Date()); createData(creditCard); } private static void createData(Payment instance) { Session session = HibernateUtil.getSession(); session.beginTransaction(); session.save(instance); session.getTransaction().commit(); } } 

then the data will be saved as

 query> select * from Payment; +----+--------+ | id | amount | +----+--------+ | 1 | 52.6 | +----+--------+ 1 row in set (0.00 sec) select * from CreditCard; +----+--------+------------------+---------------------+ | id | amount | ccNumber | expireDate | +----+--------+------------------+---------------------+ | 2 | 10 | 2536985474561236 | 2017-03-12 14:10:15 | +----+--------+------------------+---------------------+ 1 row in set (0.00 sec) 

There are three types of inheritance used in sleep mode, here is the hibernate doc for inheritance https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/InheritanceType.html , you must choose any of them according to with your requirements.

+4
source share

I was able to do something with your question. I defined another class hierarchy: UserWithRole, which extends a user that is similar to yours.

Define the User class as an object with the inheritance strategy SINGLE_TABLE :

 @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Table(name = "USERS") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) protected Long id; @Column(nullable = false) protected String name; // toString(), default constructor, setters/getters, more constructors. ... } 

This inheritance strategy has one major drawback:

  • Subclasses cannot contain nullable columns.

There is another JOINED strategy that allows you to create JOINED columns in subclasses. It creates an additional table for each subclass; these tables have FK in the superclass table.

Define the UserWithRole class:

 @Entity public class UserWithRole extends User { private String role; // toString(), default constructor, setters/getters, more constructors. ... } 

Add a Helper class to create users in the database and use your query:

 @Component public class Helper { @Autowired EntityManager entityManager; @Transactional public void createUsers() { for (long i = 0; i < 10; i++) { User user; if (i % 2 == 0) { user = new UserWithRole("User-" + i, "Role-" + i); } else { user = new User("User-" + i); } entityManager.persist(user); } entityManager.flush(); } @Transactional(readOnly = true) @SuppressWarnings("unchecked") public < T > List < T > get(Class < T > aClass) { SessionFactory sessionFactory = entityManager.getEntityManagerFactory().unwrap(SessionFactory.class); ClassMetadata hibernateMetadata = sessionFactory.getClassMetadata(aClass); if (hibernateMetadata == null) { return null; } if (hibernateMetadata instanceof AbstractEntityPersister) { AbstractEntityPersister persister = (AbstractEntityPersister) hibernateMetadata; String entityName = persister.getEntityName(); if (entityName != null) { return sessionFactory.getCurrentSession(). createQuery("from " + entityName).list(); } } return null; } } 

As you can see, I changed my method a bit:

  • A generic type has been added to avoid unsafe types;
  • The entity name used instead of the table name because HQL expects the entity name.

Let the testing begin

Get all instances of User :

 @Test public void testQueryUsers() { helper.createUsers(); for (User user: helper.get(User.class)) { System.out.println(user); } } 

Output (users with the role still have UserWithProfile instances at runtime):

 UserWithRole{id=1, name='User-0', role='Role-0'} User{id=2, name='User-1'} UserWithRole{id=3, name='User-2', role='Role-2'} User{id=4, name='User-3'} UserWithRole{id=5, name='User-4', role='Role-4'} User{id=6, name='User-5'} UserWithRole{id=7, name='User-6', role='Role-6'} User{id=8, name='User-7'} UserWithRole{id=9, name='User-8', role='Role-8'} User{id=10, name='User-9'} 

Hibernate SQL query:

 select user0_.id as id2_0_, user0_.name as name3_0_, user0_.role as role4_0_, user0_.dtype as dtype1_0_ from users user0_ 

Get all instances of UserWithProfile :

 @Test public void testQueryUsersWithProfile() { helper.createUsers(); for (User user: helper.get(UserWithRole.class)) { System.out.println(user); } } 

Output:

 UserWithRole{id=1, name='User-0', role='Role-0'} UserWithRole{id=3, name='User-2', role='Role-2'} UserWithRole{id=5, name='User-4', role='Role-4'} UserWithRole{id=7, name='User-6', role='Role-6'} UserWithRole{id=9, name='User-8', role='Role-8'} 

Hibernate SQL query:

 select userwithro0_.id as id2_0_, userwithro0_.name as name3_0_, userwithro0_.role as role4_0_ from users userwithro0_ where userwithro0_.dtype = 'UserWithRole' 

Please let me know if this is what you were looking for.

+3
source share

Good question, I suppose you should look at @MappedSuperclass anotation. This allows you to create an "abstract" entity class that will not be created, however you can continue it.

See an example here.

Update:. Since you use @Entity anonymity, you do not need to use @Table , since Hibernate will create a table for you named @Entity .

+2
source share

You can have a common superclass for both objects:

 @MappedSuperclass public abstract class AbstractTableObject { // common mappings } @Entity @Table(name = "someTable") public class TableObject extends AbstractTableObject { // remaining mappings } @Entity @Table(name = "someTable") @Immutable public class SimpleTableObject extends AbstractTableObject { // nothing here } 

In addition, you can mark the SimpleTableObject as @Immutable as shown above so that it cannot be saved or updated accidentally.

+2
source share

The comment is too long, so I have to write it in the answer.

Will you have two separate DAOs to extract TableObject and SimpleTableObject separately? If so, you do not need an extension, just treat them as two separate objects. If not, you can first get a TableObject and possibly create a constructor with SimpleTableObject(TableObject) and copy all the fields you need. I do not think that it is possible to restore two objects using one findById function. Hibernation will be confused as to which object maps to the identifier, which means that it makes no sense to have an extension. If you want to maintain consistency, how about treating SimpleTableObject as a representation of TableObjec.

UPDATE : @Quillion I don't think this is possible. Since these two objects are basically identical (you can ignore some fields in the entity), there is no way for sleep mode to identify which entity should be mapped. Of course, you can add an extra column as a discriminator, and then use @Inheritance(strategy=InheritanceType.SINGLE_TABLE) to match these two objects, but I assume that this is not what you want.

+1
source share

All Articles