POJO Models
JPA 2.1 要求 Entity class 需要满足:
- 用 @Entity 注解
- 至少有个 public or protected no-argument constructor 可以有其它构造器
- 需要是 top-level class 不能是内部类(内部静态类也不行)
- 不可以是 enum or interface
- 不可以是 final 类, methods or persistent instance variables 不可以是 final 的
- 如果要远程(remotely)使用,则要实现 Serializable 接口
- 可以是抽象类。可以继承自非 Entity 类。也可以继承自 Entity 类。
- Entity 的 persistent state 表示为 instance variables ,只可以通过 entity 的 getter/setter 访问。
Hibernate 则不用像 JPA 那么严格:
- 无参构造器可以是 package visibility
- 不需要是 top-level class
- Hibernate 可以允许 final classes or classes with final persistent state accessor (getter/setter) methods 但不推荐,因为这样将不能生成延迟加载代理
- Hibernate does not restrict the application developer from exposing instance variables and reference them from outside the entity class itself. The validity of such a paradigm, however, is debatable at best.
Prefer non-final classes
entity class 以及 persistent attribute getters and setters 尽可能不是 final 的。
从 5.0 起, Hibernate 使用更 robust 的 bytecode enhancement 用于 lazy loading 。
Implement a no-argument constructor
如果 SecurityManager 允许 override visibility ,则 Hibernate 不关心无参构造器的 visibility 。
如果要使用 runtime proxy generation 则无参构造器应该至少是 package 可见级别。
Declare getters and setters for persistent attributes
JPA 需要对所有 persistent attributes 定义 getters and setters 。
Hibernate 可以直接存取 entity fields 。
Attributes (whether fields or getters/setters) 不需要是 public 的, Hibernate 可以处理 public, protected, package or private visibility 的 attributes 。但如果要使用 runtime proxy generation 则需要至少是 package visibility
Provide identifier attribute
需要有个 identifier attributes
identifier attribute 不需要映射到物理数据库的 PK 字段,但至少应该是 UK
@Id 注解的位置定义了 persistence state access strategy
- field
- getters
Mapping the entity
@Entity.name 定义了 JPQL 查询中用到的 entity name ,默认是类名。
Implementing equals() and hashCode()
a class that acts as an identifier must implement equals/hashCode based on the id value(s).
Hibernate guarantees equivalence of persistent identity (database row) and Java identity inside a particular session scope.
Scope of identity
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
em.clear();
Person p1 = em.find(Person.class, person.id);
Person p2 = em.find(Person.class, person.id);
// true
System.out.println(p1 == p2);
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
Integer id;
String name;
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, name varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Person (name, id) values ('bruce', 1)
select test_perso0_.id as id1_0_0_, test_perso0_.name as name2_0_0_ from Person test_perso0_ where test_perso0_.id=1
Mixed Sessions
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
em.close();
EntityManager em1 = factory.createEntityManager();
Person p1 = em1.find(Person.class, person.id);
EntityManager em2 = factory.createEntityManager();
Person p2 = em2.find(Person.class, person.id);
// false
System.out.println(p1 == p2);
em1.close();
em2.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
Integer id;
String name;
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, name varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Person (name, id) values ('bruce', 1)
select test_perso0_.id as id1_0_0_, test_perso0_.name as name2_0_0_ from Person test_perso0_ where test_perso0_.id=1
select test_perso0_.id as id1_0_0_, test_perso0_.name as name2_0_0_ from Person test_perso0_ where test_perso0_.id=1
Sets with transient entities
package org.example.demo.hibernate;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "bruce";
Person p2 = new Person();
p2.name = "bruce";
Club club = new Club();
club.members.add(p1);
club.members.add(p2);
// [org.example.demo.hibernate.Test$Person@15db9742, org.example.demo.hibernate.Test$Person@6d06d69c]
System.out.println(club.members);
}
public static class Person {
Integer id;
String name;
}
public static class Club {
Set<Person> members = new HashSet<Person>();
}
}
In cases where you will be dealing with entities outside of a Session (whether they be transient or detached), especially in cases where you will be using them in Java collections, you should consider implementing equals/hashCode.
Still trouble
package org.example.demo.hibernate;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "bruce";
Person p2 = new Person();
p2.name = "bruce";
Club club = new Club();
club.members.add(p1);
club.members.add(p2);
// [org.example.demo.hibernate.Test$Person@1f]
System.out.println(club.members);
// true
System.out.println(club.members.contains(p1));
// true
System.out.println(club.members.contains(p2));
}
public static class Person {
Integer id;
String name;
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Person)) {
return false;
}
Person person = (Person) o;
return Objects.equals(id, person.id);
}
}
public static class Club {
Set<Person> members = new HashSet<Person>();
}
}
The issue here is a conflict between the use of generated identifier, the contract of Set and the equals/hashCode implementations. Set says that the equals/hashCode value for an object should not change while the object is part of the Set. But that is exactly what happened here because the equals/hasCode are based on the (generated) id, which was not set until the session.getTransaction().commit() call.
Note that this is just a concern when using generated identifiers. If you are using assigned identifiers this will not be a problem, assuming the identifier value is assigned prior to adding to the Set.
所以:
- a class that acts as an identifier
- dealing with entities outside of a Session (whether they be transient or detached), especially in cases where you will be using them in Java collections - 即直接或者间接调用 entity 的 equals/hashCode 方法。例如 entity1.equals(entity2) ,或者加入到集合。 Java 集合类需要 element 实现 equals/hashCode 。 List 会用到 equals, Set/Map 会用到 hashCode 需要实现 equals/hashCode
如果实现 equals/hashCode
- 使用 natural-id or business-key - 缺陷是:有时只有 id 是 UK 没有其它栏位是 UK
- 使用 id - 缺陷是:对 generated identifiers 则在 transient 时 id 为 null 。解决办法
- 先 generate(entityManager.persist) 再添加到集合。或者
- provide a constant value for hashCode so that the hash code value does not change before and after the entity is flushed. 或者
- 只对 non-transient entities 才 compare the entity identifier equality
测试 provide a constant value for hashCode 不行
package org.example.demo.hibernate;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "bruce";
Person p2 = new Person();
p2.name = "bruce";
Club club = new Club();
club.members.add(p1);
club.members.add(p2);
// [org.example.demo.hibernate.Test$Person@1]
System.out.println(club.members);
// true
System.out.println(club.members.contains(p1));
// true
System.out.println(club.members.contains(p2));
}
public static class Person {
Integer id;
String name;
@Override
public int hashCode() {
return 1;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Person)) {
return false;
}
Person person = (Person) o;
return Objects.equals(id, person.id);
}
}
public static class Club {
Set<Person> members = new HashSet<Person>();
}
}
Mapping optimistic locking
optimistic locking 类型:
- int or Integer
- short or Short
- long or Long
- java.sql.Timestamp
下面例子还可用 java.time.Instant
@Version annotation mapping
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
import javax.persistence.Version;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
Integer id;
@Version
private Integer version;
String name;
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, name varchar(255), version int4, primary key (id))
select nextval ('hibernate_sequence')
insert into Person (name, version, id) values ('bruce', 0, 1)
package org.example.demo.hibernate;
import java.sql.Timestamp;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
import javax.persistence.Version;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
Integer id;
@Version
private Timestamp ts;
String name;
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, name varchar(255), ts timestamp, primary key (id))
select nextval ('hibernate_sequence')
insert into Person (name, ts, id) values ('bruce', '2017-07-01 14:54:22', 1)
package org.example.demo.hibernate;
import java.time.Instant;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
import javax.persistence.Version;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
Integer id;
@Version
private Instant ts;
String name;
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, name varchar(255), ts timestamp, primary key (id))
select nextval ('hibernate_sequence')
insert into Person (name, ts, id) values ('bruce', '2017-07-01 14:55:11', 1)
Versionless optimistic locking
Hibernate supports a form of optimistic locking that does not require a dedicated "version attribute". This is also useful for use with modeling legacy schemas.
OptimisticLockType 有四个值
- NONE - optimistic locking is disabled even if there is a @Version annotation present
- VERSION - 默认
- DIRTY
- ALL
Versionless optimistic locking using OptimisticLockType.ALL
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
person.country = "China";
em.getTransaction().begin();
em.persist(person);
em.flush();
person.name = "Bruce";
em.getTransaction().commit();
em.close();
factory.close();
}
@Entity(name = "Person")
@OptimisticLocking(type = OptimisticLockType.ALL)
@DynamicUpdate
public static class Person {
@Id
@GeneratedValue
Integer id;
String name;
String country;
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, country varchar(255), name varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Person (country, name, id) values ('China', 'bruce', 1)
update Person set name='Bruce' where id=1 and country='China' and name='bruce'
注意,必须有 @DynamicUpdate
否则抛异常 org.hibernate.MappingException: optimistic-lock=all|dirty requires dynamic-update="true": org.example.demo.hibernate.Test$Person
@DynamicUpdate
的意思应该是指生成的 update 语句不是标准的 update 语句,要动态生成。
Versionless optimistic locking using OptimisticLockType.DIRTY
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.SelectBeforeUpdate;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
person.country = "China";
em.getTransaction().begin();
em.persist(person);
em.flush();
person.name = "Bruce";
em.getTransaction().commit();
em.close();
factory.close();
}
@Entity(name = "Person")
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
@SelectBeforeUpdate
public static class Person {
@Id
@GeneratedValue
Integer id;
String name;
String country;
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, country varchar(255), name varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Person (country, name, id) values ('China', 'bruce', 1)
update Person set name='Bruce' where id=1 and name='bruce'
OptimisticLockType.DIRTY 的好处是 minimize the risk of OptimisticLockException across non-overlapping entity property changes.
去掉 @SelectBeforeUpdate
生成 SQL 不变。文档中说 @SelectBeforeUpdate annotation so that detached entities are properly handled by the Session#update(entity) operation.
但是,对于真正的 detach 后再 update reattach 的情况, OptimisticLockType.DIRTY/ALL 会失去作用!!!
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;
public class Test {
public static void main(String[] args) {
ServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
.applySetting("hibernate.connection.url", "jdbc:p6spy:postgresql://localhost:5432/test")
.applySetting("hibernate.connection.username", "postgres")
.applySetting("hibernate.connection.password", "postgres")
.applySetting("hibernate.hbm2ddl.auto", "create").build();
Metadata metadata = new MetadataSources(standardRegistry).addAnnotatedClass(Person.class).getMetadataBuilder()
.build();
SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();
Session session = sessionFactory.openSession();
Person person = new Person();
person.name = "bruce";
person.country = "China";
session.beginTransaction();
session.persist(person);
session.getTransaction().commit();
session.clear();
session.beginTransaction();
person.name = "测试人";
session.saveOrUpdate(person);
session.getTransaction().commit();
session.close();
sessionFactory.close();
}
@Entity(name = "Person")
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
@SelectBeforeUpdate
public static class Person {
@Id
@GeneratedValue
Integer id;
String name;
String country;
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, country varchar(255), name varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Person (country, name, id) values ('China', 'bruce', 1)
select test_perso_.id, test_perso_.country as country2_0_, test_perso_.name as name3_0_ from Person test_perso_ where test_perso_.id=1
update Person set name='测试人' where id=1
可以看到 @SelectBeforeUpdate 倒是起作用了,但 @OptimisticLocking(type = OptimisticLockType.DIRTY)
没有起作用。
改为 @OptimisticLocking(type = OptimisticLockType.ALL)
生成 SQL 完全一样!!!
更改第 42 行 session.saveOrUpdate(person);
为 session.merge(person);
则生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, country varchar(255), name varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Person (country, name, id) values ('China', 'bruce', 1)
select test_perso0_.id as id1_0_0_, test_perso0_.country as country2_0_0_, test_perso0_.name as name3_0_0_ from Person test_perso0_ where test_perso0_.id=1
update Person set name='测试人' where id=1 and country='China' and name='bruce'
这时 @OptimisticLocking(type = OptimisticLockType.ALL)
起作用了,但 @SelectBeforeUpdate
又不起作用了。
Access strategies
Field-based access
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
import javax.persistence.Transient;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
em.clear();
em.find(Person.class, person.id);
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
Integer id;
String name;
@Transient
String zz;
public Integer getPersonId() {
System.out.println("getPersonId");
return id;
}
public void setPersonId(Integer id) {
System.out.println("setPersonId");
this.id = id;
}
public String getPersonName() {
System.out.println("getPersonName");
return name;
}
public void setPersonName(String name) {
System.out.println("setPersonName");
this.name = name;
}
public String getXx() {
System.out.println("getXx");
return "yy";
}
public void setXx(String xx) {
System.out.println("setXx");
}
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int4 not null, name varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Person (name, id) values ('bruce', 1)
select test_perso0_.id as id1_0_0_, test_perso0_.name as name2_0_0_ from Person test_perso0_ where test_perso0_.id=1
field-based access 的好处
- adding other entity-level methods is much more flexible - 例如例子中的 getXx/setXx
- some entity attributes can be hidden from outside the entity(通过 omit the getter and the setter ,这样外部就不可访问此字段了) - 例如 @Version 字段
Property-based access
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
import javax.persistence.Transient;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
em.clear();
em.find(Person.class, person.id);
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
Integer id;
String name;
String zz;
@Id
@GeneratedValue
public Integer getPersonId() {
System.out.println("getPersonId");
return id;
}
public void setPersonId(Integer id) {
System.out.println("setPersonId");
this.id = id;
}
public String getPersonName() {
System.out.println("getPersonName");
return name;
}
public void setPersonName(String name) {
System.out.println("setPersonName");
this.name = name;
}
@Transient
public String getXx() {
System.out.println("getXx");
return "yy";
}
public void setXx(String xx) {
System.out.println("setXx");
}
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (personId int4 not null, personName varchar(255), primary key (personId))
select nextval ('hibernate_sequence')
insert into Person (personName, personId) values ('bruce', 1)
select test_perso0_.personId as personId1_0_0_, test_perso0_.personName as personNa2_0_0_ from Person test_perso0_ where test_perso0_.personId=1
Every other method (e.g. helper methods for synchronizing both ends of a bidirectional one-to-many association) will have to be marked with the @Transient annotation.
Overriding the default access strategy
package org.example.demo.hibernate;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
person.name = "bruce";
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
em.clear();
em.find(Person.class, person.id);
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
Integer id;
String name;
@Access(AccessType.FIELD)
String zz;
@Id
@GeneratedValue
public Integer getPersonId() {
System.out.println("getPersonId");
return id;
}
public void setPersonId(Integer id) {
System.out.println("setPersonId");
this.id = id;
}
public String getPersonName() {
System.out.println("getPersonName");
return name;
}
public void setPersonName(String name) {
System.out.println("setPersonName");
this.name = name;
}
}
}
生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (personId int4 not null, personName varchar(255), zz varchar(255), primary key (personId))
select nextval ('hibernate_sequence')
insert into Person (personName, zz, personId) values ('bruce', NULL, 1)
select test_perso0_.personId as personId1_0_0_, test_perso0_.personName as personNa2_0_0_, test_perso0_.zz as zz3_0_0_ from Person test_perso0_ where test_perso0_.personId=1
Embeddable types and access strategy
package org.example.demo.hibernate;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Patch patch = new Patch();
Change change = new Change();
patch.change = change;
em.getTransaction().begin();
em.persist(patch);
em.getTransaction().commit();
em.clear();
em.find(Patch.class, patch.id);
em.close();
factory.close();
}
@Entity
public static class Patch {
@Id
@GeneratedValue
private Long id;
@Embedded
private Change change;
}
@Embeddable
@Access(AccessType.PROPERTY)
public static class Change {
public String getDiff() {
System.out.println("getDiff");
return "xx";
}
public void setDiff(String diff) {
System.out.println("setDiff");
}
}
}
生成 SQL
drop table if exists Test$Patch cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Test$Patch (id int8 not null, diff varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Test$Patch (diff, id) values ('xx', 1)
select test_patch0_.id as id1_0_0_, test_patch0_.diff as diff2_0_0_ from Test$Patch test_patch0_ where test_patch0_.id=1
Entity including a collection of embeddable types
package org.example.demo.hibernate;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OrderColumn;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Patch patch = new Patch();
Change change1 = new Change();
Change change2 = new Change();
patch.changes.add(change1);
patch.changes.add(change2);
em.getTransaction().begin();
em.persist(patch);
em.getTransaction().commit();
em.clear();
em.find(Patch.class, patch.id);
em.close();
factory.close();
}
@Entity
public static class Patch {
@Id
@GeneratedValue
private Long id;
@ElementCollection
@CollectionTable(name = "patch_change", joinColumns = @JoinColumn(name = "patch_id"))
@OrderColumn(name = "index_id")
private List<Change> changes = new ArrayList<>();
}
@Embeddable
@Access(AccessType.PROPERTY)
public static class Change {
public String getDiff() {
System.out.println("getDiff");
return "xx";
}
public void setDiff(String diff) {
System.out.println("setDiff");
}
}
}
生成 SQL
alter table patch_change drop constraint FKd2ccg0c1tftjv98yadab497qy
drop table if exists patch_change cascade
drop table if exists Test$Patch cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table patch_change (patch_id int8 not null, diff varchar(255), index_id int4 not null, primary key (patch_id, index_id))
create table Test$Patch (id int8 not null, primary key (id))
alter table patch_change add constraint FKd2ccg0c1tftjv98yadab497qy foreign key (patch_id) references Test$Patch
select nextval ('hibernate_sequence')
insert into Test$Patch (id) values (1)
insert into patch_change (patch_id, index_id, diff) values (1, 0, 'xx')
insert into patch_change (patch_id, index_id, diff) values (1, 1, 'xx')
select test_patch0_.id as id1_1_0_ from Test$Patch test_patch0_ where test_patch0_.id=1