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

results matching ""

    No results matching ""