简体   繁体   English

Hibernate扩展了同一个表的实体

[英]Hibernate extend entity for same table

I have a table with two fields I would like to have two objects. 我有一个有两个字段的表我希望有两个对象。

First one only has field1 第一个只有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;

Second one 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 each one 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;
}

What I would like to do is have TableObject extend SimpleTableObject . 我想做的是让TableObject扩展SimpleTableObject How would I go about doing that? 我该怎么做呢?

I was able to do something about your question. 我能够对你的问题做些什么。 I defined another class hierarchy: UserWithRole that extends User which is similar to yours. 我定义了另一个类层次结构:UserWithRole,它扩展了User,它类似于你的。

Define class User as an entity with inheritance strategy SINGLE_TABLE : 将类User定义为具有继承策略的实体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. 还有另一种策略JOINED允许在子类中创建不可为空的列。 It creates an additional table for each subclass these tables have FK to the superclass table. 它为每个子类创建一个附加表,这些表具有FK到超类表。

Define UserWithRole class: 定义UserWithRole类:

@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: 添加Helper类以在数据库中创建用户并使用您的查询:

@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. 使用的实体名称而不是表名,因为HQL需要实体名称。

Let's start testing 我们开始测试了

Get all User instances: 获取所有User实例:

@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): 输出(具有角色的用户仍然是运行时的UserWithProfile实例):

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: Hibernate发出的SQL查询:

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: 获取所有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'}

SQL query issued by Hibernate: Hibernate发出的SQL查询:

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. 请告诉我这是你要找的。

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. 此外,您可以将SimpleTableObject实体标记为@Immutable如上所示,以便不会意外地@Immutable或更新它。

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: 如果你想在你需要的表中保存公共字段意味着假设你有A类和B类,并且你有一些常见的字段,比如created_by,updated_by,你想在两个实体中保存field1,field2:IN数据库级别:

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 对于这种类型的结构,您应该使用@MapedSuperclass作为@Dragan Bozanovic建议

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 eg: Suppose you have 2 class Payment and CreditCard, Payment is parent class for CreditCard. 但是如果你想要Parent Child关系并希望每个类生成表,那么你可以使用@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)它将为每个类创建表格例如:假设你有2个类Payment和CreditCard,Payment是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. 在hibernate中使用了3种类型的继承,这里是hibernate doc for inheritance https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/InheritanceType.html ,你应该根据它选择任何一种你的要求。

Good question, I assume you have to look at @MappedSuperclass anotation. 好问题,我假设您必须查看@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. 更新:由于你使用@Entity anotation,你不必使用@Table因为Hibernate会为你创建一个名为@Entity表。

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. 你是否会有两个单独的DAO来分别检索TableObjectSimpleTableObject ?如果是,你不需要扩展,只需将它们视为两个独立的实体。 If not, you can first get the TableObject and maybe create a constructor with SimpleTableObject(TableObject) and copy all the fields you need there. 如果没有,您可以先获取TableObject,也可以使用SimpleTableObject(TableObject)创建构造函数,并复制所需的所有字段。 I don't think it is possible to retrieve two entities with one findById function. 我认为用一个findById函数检索两个实体是不可能的。 Hibernate will be confused which entity to map to with the id, which means it doesn't make much sense to have the extend. Hibernate会混淆使用id映射到哪个实体,这意味着拥有扩展名没有多大意义。 If you want to keep the consistency, how about considering SimpleTableObject as a view of TableObjec. 如果要保持一致性,请将SimpleTableObject视为TableObjec的视图。

UPDATE : @Quillion I don't think it is possible this way. 更新 :@Quillion我认为不可能这样。 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. 由于这两个实体基本相同(您可以忽略实体中的某些字段),因此hibernate无法识别要映射的实体。 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. 当然你可以添加一个额外的列作为鉴别器,然后使用@Inheritance(strategy=InheritanceType.SINGLE_TABLE)映射这两个实体,但我认为这不是你想要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM