Hibernate extend entity for same table

前端 未结 5 1057
猫巷女王i
猫巷女王i 2021-02-05 22:32

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

First one only has field1

@Entity(name = \"simpleTableObject\")
@Table(name = \"someTabl         


        
相关标签:
5条回答
  • 2021-02-05 23:14

    I was able to do something about your question. I defined another class hierarchy: UserWithRole that extends User which is similar to yours.

    Define class User as an entity with 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 considerable disadvantage:

    • Subclasses can't contain non-nullable columns.

    There is another strategy JOINED that allows creating non-nullable columns in subclasses. It creates an additional table for each subclass these tables have FK to the superclass table.

    Define UserWithRole class:

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

    Add 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 may see, I changed your method a bit:

    • Added generic type to avoid unsafe type casts;
    • Used entity name instead of table name because HQL expects entity name.

    Let's start testing

    Get all User instances:

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

    Output (users with role are still 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'}
    

    SQL query issued by Hibernate:

    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 UserWithProfile instances:

    @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'}
    

    SQL query issued by Hibernate:

    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 it's what you were looking for.

    0 讨论(0)
  • 2021-02-05 23:17

    If you want save common fields in your required tables means suppose you have class A and B and you have some common filed like created_by,updated_by and you want to save field1,field2 in both entity: IN database level:

    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 @Dragan Bozanovic suggested

    But if you want Parent Child relation and want generate table per class then you can use @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) it will create table per class e.g: Suppose you have 2 class 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 will save 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 data will save like

    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 3 types of inheritance used in hibernate, here is hibernate doc for inheritance https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/InheritanceType.html, you should pick any of them according to your requirement.

    0 讨论(0)
  • 2021-02-05 23:20

    Good question, I assume you have to look at @MappedSuperclass anotation. It allows to make an "abstract" entity class that would not be instantiated, however you can extend from that.

    See example of that here

    Update: Since you use @Entity anotation, you dont have to use @Table since Hibernate will create table named @Entity for you.

    0 讨论(0)
  • 2021-02-05 23:27

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

    Are you going to have two separate DAO to retrieve TableObject and SimpleTableObject separately?If yes, you don't need the extend, just treat them as two separate entities. If not, you can first get the TableObject and maybe create a constructor with SimpleTableObject(TableObject) and copy all the fields you need there. I don't think it is possible to retrieve two entities with one findById function. Hibernate will be confused which entity to map to with the id, which means it doesn't make much sense to have the extend. If you want to keep the consistency, how about considering SimpleTableObject as a view of TableObjec.

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

    0 讨论(0)
  • 2021-02-05 23:32

    You can have a common superclass for both entities:

    @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
    }
    

    Additionally, you can mark the SimpleTableObject entity to be @Immutable as shown above, so that it cannot be persisted or updated accidentally.

    0 讨论(0)
提交回复
热议问题