Hibernate扩展了同一个表的实体

时间:2017-03-05 14:24:17

标签: java hibernate

我有一个包含两个字段的表 我想有两个对象。

第一个只有field1

git psh -f

第二个有两个字段

$('#net-input').change(function() {
    var gross;
    var net = $(this).val;
    // ..calculate gross..
    $('gross-input').val(gross);
}

我使用

加载每一个
@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;

我想要做的是@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; 延长@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; } 。我该怎么做呢?

5 个答案:

答案 0 :(得分:6)

我能够对你的问题做些什么。我定义了另一个类层次结构:UserWithRole扩展了User,它类似于你的。

将类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.
 ...
}

这种继承策略有一个相当大的缺点:

  • 子类不能包含不可为空的列。

还有另一种策略JOINED允许在子类中创建不可为空的列。它为每个子类创建一个附加表,这些表具有FK到超类表。

定义UserWithRole类:

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

添加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;
 }

}

正如您所看到的,我稍微改变了您的方法:

  • 添加了通用类型以避免不安全的类型转换;
  • 使用的实体名称而不是表名,因为HQL需要实体名称。

让我们开始测试

获取所有User个实例:

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

输出(具有角色的用户在运行时仍为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'}

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_

获取所有UserWithProfile个实例:

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

输出:

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查询:

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

请告诉我这是你要找的。

答案 1 :(得分:5)

两个实体都可以有一个共同的超类:

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

此外,您可以将SimpleTableObject实体标记为@Immutable,如上所示,以便无法持续或不时更新。

答案 2 :(得分:4)

如果你想在你需要的表中保存公共字段意味着假设你有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        |
+----+------------+------------+

对于这种类型的结构你应该使用@MappedSuperclass作为@Dragan Bozanovic建议

但是如果你想要父子关系,并希望每个类生成表,那么你可以使用 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 它将为每个类创建表 例如:假设您有2类付款和信用卡,付款是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;
    }
}

现在您将保存日期:

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();
    }
}

然后数据将保存为

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)

hibernate中使用了3种类型的继承,这里是hibernate doc for inheritance https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/InheritanceType.html,你应该根据你的要求选择其中任何一个。

答案 3 :(得分:2)

好问题,我假设您必须查看@MappedSuperclass anotation。它允许制作一个抽象的&#34;不会实例化的实体类,但是你可以从中扩展。

参见here

的示例

更新:由于您使用@Entity anotation,因此您不必使用@Table,因为Hibernate将为您创建名为@Entity的表。

答案 4 :(得分:1)

评论有点太长了,所以我必须在答案中写下来。

你是否会有两个单独的DAO来分别检索TableObjectSimpleTableObject?如果是,你不需要扩展,只需将它们视为两个独立的实体。如果没有,您可以先获取TableObject,然后创建一个带SimpleTableObject(TableObject)的构造函数,并复制您需要的所有字段。我不认为可以使用一个findById函数检索两个实体。 Hibernate会混淆使用id映射到哪个实体,这意味着拥有扩展名没有多大意义。如果要保持一致性,请将SimpleTableObject视为TableObjec的视图。

更新:@Quillion我认为不可能这样。由于这两个实体基本相同(您可以忽略实体中的某些字段),因此hibernate无法识别要映射的实体。当然你可以添加一个额外的列作为鉴别器,然后使用@Inheritance(strategy=InheritanceType.SINGLE_TABLE)映射这两个实体,但我认为这不是你想要的。