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

results matching ""

    No results matching ""