原文: https://howtodoinjava.com/hibernate/immutable-and-naturalid-hibernate-specific-annotations/

在上一篇文章中,我们了解了Hiberate中最常用的 JPA 注解,还了解了与这些 JPA 注解互补的 Hiberate 注解。 使用 JPA 注解使您的应用代码可移植到其他 JPA 实现中,这是一件好事。 除了 JPA 注解之外,Hiberate 还具有一些自己的注解,您可以使用它们在应用代码中具有某些功能。 但是您必须记住,将来的日期可能很难使您的代码可移植。

阅读更多: JPA2 持久化注解教程

在本文中,我们将学习两个特定于 Hibernate 的此类注解。

1)@Immutable注解

@Immutable注解将一个实体标记为不可变。 这在您的实体表示参考数据的情况下非常有用,例如状态列表,性别或其他很少突变的数据。

由于状态之类的东西很少会更改,因此通常有人会通过 SQL 或管理应用手动更新数据。 Hibernate 可以积极地缓存此数据,需要将其考虑在内。 如果参考数据发生变化,则需要确保已通知使用该数据的应用(可以使用refresh()方法)或以某种方式重新启动。

@Immutable注解告诉 Hibernate,对不可变实体的任何更新都不应传递给数据库而不会给出任何错误。 @Immutable也可以放在集合上; 在这种情况下,对集合的更改(添加或删除)将引发HibernateException

EmployeeEntity.java

  1. import org.hibernate.annotations.Immutable;
  2. @Immutable
  3. @Entity
  4. @Table(name = "Employee")
  5. public class EmployeeEntity implements Serializable
  6. {
  7. private static final long serialVersionUID = -1798070786993154676L;
  8. @Id
  9. @Column(name = "ID", unique = true, nullable = false)
  10. private Integer employeeId;
  11. @Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
  12. private String firstName;
  13. @Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
  14. private String lastName;
  15. //Setters and Getters
  16. }

ImmutableAnnotationExample.java

  1. public class ImmutableAnnotationExample
  2. {
  3. public static void main(String[] args)
  4. {
  5. setupTestData();
  6. Session sessionOne = HibernateUtil.getSessionFactory().openSession();
  7. sessionOne.beginTransaction();
  8. //Load the employee in another session
  9. EmployeeEntity employee = (EmployeeEntity) sessionOne.load(EmployeeEntity.class, 1);
  10. //Update the first name
  11. employee.setFirstName("Alex");
  12. sessionOne.flush();
  13. sessionOne.close();
  14. Session sessionTwo = HibernateUtil.getSessionFactory().openSession();
  15. sessionTwo.beginTransaction();
  16. //Load the employee in another session
  17. EmployeeEntity employeeUpdated = (EmployeeEntity) sessionTwo.load(EmployeeEntity.class, 1);
  18. //Verify the first name
  19. System.out.println(employeeUpdated.getFirstName());
  20. sessionTwo.flush();
  21. sessionTwo.close();
  22. HibernateUtil.shutdown();
  23. }
  24. private static void setupTestData(){
  25. Session session = HibernateUtil.getSessionFactory().openSession();
  26. session.beginTransaction();
  27. //Create Employee
  28. EmployeeEntity emp = new EmployeeEntity();
  29. emp.setEmployeeId(1);
  30. emp.setFirstName("Lokesh");
  31. emp.setLastName("Gupta");
  32. session.save(emp);
  33. session.getTransaction().commit();
  34. session.close();
  35. }
  36. }
  37. Output:
  38. Hibernate: insert into Employee (FIRST_NAME, LAST_NAME, ID) values (?, ?, ?)
  39. Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_, employeeen0_.LAST_NAME as LAST_NAM3_1_0_
  40. from Employee employeeen0_ where employeeen0_.ID=?
  41. Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_, employeeen0_.LAST_NAME as LAST_NAM3_1_0_
  42. from Employee employeeen0_ where employeeen0_.ID=?
  43. Lokesh //Value didn't updated in database i.e. immutable

2)@NaturalId注解

在过去的教程中,我们了解了很多有关@Id@GeneratedValue注解以为数据库中的记录创建主键的知识。 在大多数实际应用中,这些主键是“人工主键”,并且仅在应用运行时内部引用。 但是,还存在“自然 ID”的概念,除了人工或复合主键之外,它还提供了另一种方便且合乎逻辑的方式来引用实体。

自然 ID 的示例可能是美国的社会安全号码或税号,而印度则是 PAN 号码。 实体(是个人或公司)可能具有由 Hibernate 生成的人为主键,但也可能具有唯一的税标识符。 Hibernate 还允许您基于这些自然 ID 搜索和加载实体。

对于自然 ID,有两种形式的加载机制: 一种使用简单自然 ID(其中自然 ID 是一个且仅一个字段),另一种使用命名属性作为复合自然 ID 的一部分。

简单的自然 ID 示例

  1. @Entity
  2. @Table(name = "Employee")
  3. public class EmployeeEntity implements Serializable
  4. {
  5. private static final long serialVersionUID = -1798070786993154676L;
  6. @Id
  7. @Column(name = "ID", unique = true, nullable = false)
  8. private Integer employeeId;
  9. @Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
  10. private String firstName;
  11. @Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
  12. private String lastName;
  13. //Natural id can be SSN as well
  14. @NaturalId
  15. Integer SSN;
  16. //Setters and Getters
  17. }

SimpleNaturalIdExample.java

  1. public class SimpleNaturalIdExample
  2. {
  3. public static void main(String[] args)
  4. {
  5. setupTestData();
  6. Session sessionOne = HibernateUtil.getSessionFactory().openSession();
  7. sessionOne.beginTransaction();
  8. //Load the employee
  9. EmployeeEntity employee1 = (EmployeeEntity) sessionOne.load(EmployeeEntity.class, 1);
  10. //Just to ensure that employee is loasded from DB
  11. System.out.println(employee1.getFirstName());
  12. //Get the employee for natural id i.e. SSN; This does not execute another SQL SELECT as entity is already present in session
  13. EmployeeEntity employee2 = (EmployeeEntity) sessionOne.bySimpleNaturalId(EmployeeEntity.class).load(12345);
  14. //Verify that employee1 and employee2 refer to same object
  15. assert(employee1 == employee2);
  16. sessionOne.flush();
  17. sessionOne.close();
  18. System.out.println("====================================");
  19. Session sessionTwo = HibernateUtil.getSessionFactory().openSession();
  20. sessionTwo.beginTransaction();
  21. //Get the employee for natural id i.e. SSN; entity is not present in this session
  22. EmployeeEntity employee = (EmployeeEntity) sessionTwo.bySimpleNaturalId(EmployeeEntity.class).load(12345);
  23. sessionTwo.flush();
  24. sessionTwo.close();
  25. HibernateUtil.shutdown();
  26. }
  27. private static void setupTestData(){
  28. Session session = HibernateUtil.getSessionFactory().openSession();
  29. session.beginTransaction();
  30. //Create Employee
  31. EmployeeEntity emp = new EmployeeEntity();
  32. emp.setEmployeeId(1);
  33. emp.setFirstName("Lokesh");
  34. emp.setLastName("Gupta");
  35. emp.setSSN(12345);
  36. session.save(emp);
  37. session.getTransaction().commit();
  38. session.close();
  39. }
  40. }
  41. Output:
  42. Hibernate: insert into Employee (SSN, FIRST_NAME, LAST_NAME, ID) values (?, ?, ?, ?)
  43. Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.SSN as SSN2_1_0_, employeeen0_.FIRST_NAME as FIRST_NA3_1_0_,
  44. employeeen0_.LAST_NAME as LAST_NAM4_1_0_ from Employee employeeen0_ where employeeen0_.ID=?
  45. Lokesh
  46. ====================================
  47. Hibernate: select employeeen_.ID as ID1_1_ from Employee employeeen_ where employeeen_.SSN=?
  48. Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.SSN as SSN2_1_0_, employeeen0_.FIRST_NAME as FIRST_NA3_1_0_,
  49. employeeen0_.LAST_NAME as LAST_NAM4_1_0_ from Employee employeeen0_ where employeeen0_.ID=?

请密切注意如果会话中不存在实体,并且如果您使用实体的自然 ID 获取实体,则使用自然 ID 提取第一个主要 ID; 然后使用此主要 ID 提取实体。 如果会话中已经存在实体,则返回相同实体的引用,而无需在数据库中执行其他SELECT语句。

复合自然 ID 示例

  1. @Entity
  2. @Table(name = "Employee")
  3. public class EmployeeEntity implements Serializable
  4. {
  5. private static final long serialVersionUID = -1798070786993154676L;
  6. @Id
  7. @Column(name = "ID", unique = true, nullable = false)
  8. private Integer employeeId;
  9. @Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
  10. private String firstName;
  11. @Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
  12. private String lastName;
  13. //Natural id part 1
  14. @NaturalId
  15. Integer seatNumber;
  16. //Natural id part 2
  17. @NaturalId
  18. String departmentName;
  19. //Setters and Getters
  20. }

CompositeNaturalIdExample.java

  1. public class CompositeNaturalIdExample
  2. {
  3. public static void main(String[] args)
  4. {
  5. setupTestData();
  6. Session sessionOne = HibernateUtil.getSessionFactory().openSession();
  7. sessionOne.beginTransaction();
  8. //Get the employee for natural id i.e. SSN; entity is not present in this session
  9. EmployeeEntity employee = (EmployeeEntity) sessionOne.byNaturalId(EmployeeEntity.class)
  10. .using("seatNumber", 12345)
  11. .using("departmentName", "IT")
  12. .load();
  13. System.out.println(employee.getFirstName());
  14. sessionOne.flush();
  15. sessionOne.close();
  16. HibernateUtil.shutdown();
  17. }
  18. private static void setupTestData(){
  19. Session session = HibernateUtil.getSessionFactory().openSession();
  20. session.beginTransaction();
  21. //Create Employee
  22. EmployeeEntity emp = new EmployeeEntity();
  23. emp.setEmployeeId(1);
  24. emp.setFirstName("Lokesh");
  25. emp.setLastName("Gupta");
  26. emp.setSeatNumber(12345);
  27. emp.setDepartmentName("IT");
  28. session.save(emp);
  29. session.getTransaction().commit();
  30. session.close();
  31. }
  32. }
  33. Output:
  34. Hibernate: insert into Employee (departmentName, FIRST_NAME, LAST_NAME, seatNumber, ID) values (?, ?, ?, ?, ?)
  35. Hibernate: select employeeen_.ID as ID1_1_ from Employee employeeen_ where employeeen_.departmentName=? and employeeen_.seatNumber=?
  36. Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.departmentName as departme2_1_0_, employeeen0_.FIRST_NAME as FIRST_NA3_1_0_,
  37. employeeen0_.LAST_NAME as LAST_NAM4_1_0_, employeeen0_.seatNumber as seatNumb5_1_0_ from Employee employeeen0_ where employeeen0_.ID=?
  38. Lokesh

复合自然 ID 的实体获取逻辑与简单自然 ID 相同。 除了使用多个自然键而不是一个以外,没有区别。

这些就是所有这些众所周知的注解。 继续在评论中发表您的想法。

祝您学习愉快!