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