;generated property: 该 property 的值由数据库生成。
应用程序需要手动调用 refresh 以得到 generated property 的值。如果标记 properties 为 @Generated 则会由 Hibernate 自动 refresh ,当 Hibernate 执行 insert/update 时,会立即执行一条 select 以得到 generated values
只有 @Version and @Basic 才可以标记为 @Generated 。如果标记为 @Generated 则该 property 也将 non-insertable and non-updateable
@org.hibernate.annotations.Generated.value 取值:
- GenerationTime.NEVER - 不由数据库生成。默认值。
 - GenerationTime.INSERT - 在 insert 时生成,在 update 时不生成。例如 creationTimestamp 栏位
 - GenerationTime.ALWAYS - 在 insert 和 update 时生成。
 
@Generated mapping example
首先创建表
drop sequence if exists hibernate_sequence;
drop table if exists Person;
create sequence hibernate_sequence start 1 increment 1;
create table Person (id int8 not null, firstName varchar(255), lastName varchar(255), fullName varchar(255), primary key (id));
create or replace function update_full_name() returns trigger as
$$
declare
begin
  update Person set fullName = new.firstName || ' ' || new.lastName where id = new.id;
  return null;
end;
$$
language plpgsql;
create trigger tg_person after insert or update on Person for each row
when (new.fullName is null or new.fullname <> (new.firstName || ' ' || new.lastName))
execute procedure update_full_name();
然后修改 src/main/resources/META-INF/persistence.xml 不再 drop-and-create
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
  version="2.1">
  <persistence-unit name="test">
    <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:p6spy:postgresql://localhost:5432/test" />
      <property name="javax.persistence.jdbc.user" value="postgres" />
      <property name="javax.persistence.jdbc.password" value="postgres" />
    </properties>
  </persistence-unit>
</persistence>
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.Generated;
import org.hibernate.annotations.GenerationTime;
public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();
        Person person = new Person();
        person.firstName = "John";
        person.lastName = "Doe";
        em.getTransaction().begin();
        em.persist(person);
        System.out.println("after persist");
        em.getTransaction().commit();
        System.out.println("after commit");
        em.clear();
        // John Doe
        System.out.println(person.fullName);
        em.getTransaction().begin();
        Person p = em.find(Person.class, person.id);
        p.firstName = "Bruce";
        em.flush();
        // Bruce Doe
        System.out.println(p.fullName);
        em.getTransaction().commit();
        em.close();
        factory.close();
    }
    @Entity(name = "Person")
    public static class Person {
        @Id
        @GeneratedValue
        Long id;
        String firstName;
        String lastName;
        @Generated(GenerationTime.ALWAYS)
        String fullName;
    }
}
生成的 SQL
select nextval ('hibernate_sequence')
insert into Person (firstName, lastName, id) values ('John', 'Doe', 1)
select test_perso_.fullName as fullName3_0_ from Person test_perso_ where test_perso_.id=1
select test_perso0_.id as id1_0_0_, test_perso0_.firstName as firstNam2_0_0_, test_perso0_.fullName as fullName3_0_0_, test_perso0_.lastName as lastName4_0_0_ from Person test_perso0_ where test_perso0_.id=1
update Person set firstName='Bruce', lastName='Doe' where id=1
select test_perso_.fullName as fullName3_0_ from Person test_perso_ where test_perso_.id=1
从日志可以看到 select fullName 的语句在 commit 之前执行。
@GeneratorType annotation
@org.hibernate.annotations.GeneratorType 用于自定义 generator 。
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.Session;
import org.hibernate.annotations.GenerationTime;
import org.hibernate.annotations.GeneratorType;
import org.hibernate.tuple.ValueGenerator;
public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();
        Person person = new Person();
        person.firstName = "John";
        person.lastName = "Doe";
        CurrentUser.INSTANCE.logIn("Alice");
        em.getTransaction().begin();
        em.persist(person);
        em.getTransaction().commit();
        CurrentUser.INSTANCE.logOut();
        // Alice
        System.out.println(person.createdBy);
        // Alice
        System.out.println(person.updatedBy);
        em.clear();
        CurrentUser.INSTANCE.logIn("Bob");
        em.getTransaction().begin();
        Person p = em.find(Person.class, person.id);
        p.firstName = "Bruce";
        em.getTransaction().commit();
        CurrentUser.INSTANCE.logOut();
        // Alice
        System.out.println(p.createdBy);
        // Bob
        System.out.println(p.updatedBy);
        em.close();
        factory.close();
    }
    public static class CurrentUser {
        public static final CurrentUser INSTANCE = new CurrentUser();
        private static final ThreadLocal<String> storage = new ThreadLocal<>();
        public void logIn(String user) {
            storage.set(user);
        }
        public void logOut() {
            storage.remove();
        }
        public String get() {
            return storage.get();
        }
    }
    public static class LoggedUserGenerator implements ValueGenerator<String> {
        @Override
        public String generateValue(Session session, Object owner) {
            return CurrentUser.INSTANCE.get();
        }
    }
    @Entity(name = "Person")
    public static class Person {
        @Id
        @GeneratedValue
        Long id;
        String firstName;
        String lastName;
        @GeneratorType(type = LoggedUserGenerator.class, when = GenerationTime.INSERT)
        String createdBy;
        @GeneratorType(type = LoggedUserGenerator.class, when = GenerationTime.ALWAYS)
        String updatedBy;
    }
}
生成 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 int8 not null, createdBy varchar(255), firstName varchar(255), lastName varchar(255), updatedBy varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Person (createdBy, firstName, lastName, updatedBy, id) values ('Alice', 'John', 'Doe', 'Alice', 1)
select test_perso0_.id as id1_0_0_, test_perso0_.createdBy as createdB2_0_0_, test_perso0_.firstName as firstNam3_0_0_, test_perso0_.lastName as lastName4_0_0_, test_perso0_.updatedBy as updatedB5_0_0_ from Person test_perso0_ where test_perso0_.id=1
update Person set createdBy='Alice', firstName='Bruce', lastName='Doe', updatedBy='Bob' where id=1
@CreationTimestamp annotation
@org.hibernate.annotations.CreationTimestamp 指示 Hibernate 将该 attribute 值设为当前 JVM 时间。支持的 attribute type 有:
- java.util.Date
 - java.util.Calendar
 - java.sql.Date
 - java.sql.Time
 - java.sql.Timestamp
 
package org.example.demo.hibernate;
import java.util.Date;
import javax.persistence.Column;
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.CreationTimestamp;
public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();
        Event event = new Event();
        em.getTransaction().begin();
        em.persist(event);
        em.getTransaction().commit();
        em.close();
        factory.close();
    }
    @Entity(name = "Event")
    public static class Event {
        @Id
        @GeneratedValue
        private Long id;
        @Column(name = "`timestamp`")
        @CreationTimestamp
        private Date timestamp;
    }
}
生成的 SQL
drop table if exists Event cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Event (id int8 not null, "timestamp" timestamp, primary key (id))
select nextval ('hibernate_sequence')
insert into Event ("timestamp", id) values ('2017-06-25 19:42:01', 1)
@ValueGenerationType meta-annotation
4.3 引入了 @ValueGenerationType meta annotation ,是声明 generated attributes or customizing generators 的新方式。
@Generated 也用 @ValueGenerationType 改写。
@org.hibernate.annotations.ValueGenerationType.generatedBy 返回值为 AnnotationValueGeneration 类型。自定义 generator 要实现这个接口。
内建的使用 @ValueGenerationType 标记的注解有:
- @Generated - generatedBy = GeneratedValueGeneration.class, 由数据库生成
 - @GeneratorType - generatedBy = VmValueGeneration.class, 需要自定义一个 ValueGenerator
 - @CreationTimestamp - generatedBy = CreationTimestampGeneration.class
 - @UpdateTimestamp - generatedBy = UpdateTimestampGeneration.class
 
AnnotationValueGeneration 的内建实现有:
- org.hibernate.tuple.GeneratedValueGeneration
 - org.hibernate.tuple.VmValueGeneration
 - org.hibernate.tuple.CreationTimestampGeneration
 - org.hibernate.tuple.UpdateTimestampGeneration
 
Database-generated values
package org.example.demo.hibernate;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Date;
import javax.persistence.Column;
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.ValueGenerationType;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();
        Event event = new Event();
        em.getTransaction().begin();
        em.persist(event);
        em.getTransaction().commit();
        em.close();
        factory.close();
    }
    @Entity(name = "Event")
    public static class Event {
        @Id
        @GeneratedValue
        private Long id;
        @Column(name = "`timestamp`")
        @FunctionCreationTimestamp
        private Date timestamp;
    }
    @ValueGenerationType(generatedBy = FunctionCreationValueGeneration.class)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface FunctionCreationTimestamp {
    }
    @SuppressWarnings("serial")
    public static class FunctionCreationValueGeneration implements AnnotationValueGeneration<FunctionCreationTimestamp> {
        @Override
        public void initialize(FunctionCreationTimestamp annotation, Class<?> propertyType) {
        }
        /**
         * Generate value on INSERT
         * @return when to generate the value
         */
        public GenerationTiming getGenerationTiming() {
            return GenerationTiming.INSERT;
        }
        /**
         * Returns null because the value is generated by the database.
         * @return null
         */
        public ValueGenerator<?> getValueGenerator() {
            return null;
        }
        /**
         * Returns true because the value is generated by the database.
         * @return true
         */
        public boolean referenceColumnInSql() {
            return true;
        }
        /**
         * Returns the database-generated value
         * @return database-generated value
         */
        public String getDatabaseGeneratedReferencedColumnValue() {
            return "current_timestamp";
        }
    }
}
生成 SQL
drop table if exists Event cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Event (id int8 not null, "timestamp" timestamp, primary key (id))
select nextval ('hibernate_sequence')
insert into Event ("timestamp", id) values (current_timestamp, 1)
select test_event_."timestamp" as timestam2_0_ from Event test_event_ where test_event_.id=1
In-memory-generated values
package org.example.demo.hibernate;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Date;
import javax.persistence.Column;
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.ValueGenerationType;
import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGenerator;
public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();
        Event event = new Event();
        em.getTransaction().begin();
        em.persist(event);
        em.getTransaction().commit();
        em.close();
        factory.close();
    }
    @Entity(name = "Event")
    public static class Event {
        @Id
        @GeneratedValue
        private Long id;
        @Column(name = "`timestamp`")
        @FunctionCreationTimestamp
        private Date timestamp;
    }
    @ValueGenerationType(generatedBy = FunctionCreationValueGeneration.class)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface FunctionCreationTimestamp {
    }
    @SuppressWarnings("serial")
    public static class FunctionCreationValueGeneration implements AnnotationValueGeneration<FunctionCreationTimestamp> {
        @Override
        public void initialize(FunctionCreationTimestamp annotation, Class<?> propertyType) {
        }
        /**
         * Generate value on INSERT
         * @return when to generate the value
         */
        public GenerationTiming getGenerationTiming() {
            return GenerationTiming.INSERT;
        }
        /**
         * Returns the in-memory generated value
         * @return {@code true}
         */
        public ValueGenerator<?> getValueGenerator() {
            return (session, owner) -> new Date();
        }
        /**
         * Returns false because the value is generated by the database.
         * @return false
         */
        public boolean referenceColumnInSql() {
            return false;
        }
        /**
         * Returns null because the value is generated in-memory.
         * @return null
         */
        public String getDatabaseGeneratedReferencedColumnValue() {
            return null;
        }
    }
}
生成 SQL
drop table if exists Event cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Event (id int8 not null, "timestamp" timestamp, primary key (id))
select nextval ('hibernate_sequence')
insert into Event ("timestamp", id) values ('2017-06-25 19:51:04', 1)