一、基本实体注解
文档地址: https://www.w3cschool.cn/java/jpa-entitymanager.html
1. 自动更新实体创建时间和修改时间
@Data
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class BaseEntity {
/**
* 将字段声明为protected ,千万不要使用private否则访问不到
*
* @CreatedDate 注解:创建时间字段(inster 自动设置)
* @LastModifiedDate 注解:最后修改时间字段(update 自动设置)
* @Temporal 定义时间类型 只能作用于Date类型与Calendar
*/
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
@Column(columnDefinition = "datetime comment '创建时间'")
protected Date createTime;
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(columnDefinition = "datetime comment '更新时间'")
protected Date updateTime;
@Column(columnDefinition = "bit comment '逻辑删除'")
protected Boolean deleted;
}
说明:
实体类必须添加:
@EntityListeners(AuditingEntityListener.class)
SpringBoot启动类必须加注解:
@EnableJpaAuditing
或者数据库字段中添加:
createTime : CURRENT_TIMESTAMP
modifyTime : CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
2. @MappedSuperclass 注解
继承关系共用字段。
这个注解表示在父类上面的,用来标识父类。
基于代码复用和模型分离的思想,在项目开发中使用JPA的@MappedSuperclass注解将实体类的多个属性分别封装到不同的非实体类中。例如,数据库表中都需要id来表示编号,id是这些映射实体类的通用的属性,交给jpa统一生成主键id编号,那么使用一个父类来封装这些通用属性,并用@MappedSuperclas标识。
注意:
1) 标注为@MappedSuperclass的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。
2) .标注为@MappedSuperclass的类不能再标注@Entity或@Table注解,也无需实现序列化接口。
3. @EntityListeners 实体监听器
对实体属性变化的跟踪,它提供了保存前,保存后,更新前,更新后,删除前,删除后等状态,就像是拦截器一样,可以在拦截方法里重写你的个性化逻辑。
1) 实体类监听器:
@Slf4j
public class TestEntityListeners {
/**
* 被@Prepersist注解的方法 ,完成save之前的操作。
* @param entity
*/
@PrePersist
public void PrePersist(Object entity){
log.info("开始保存--"+entity.toString());
}
/**
* 被@Preupdate注解的方法 ,完成update之前的操作。
* @param entity
*/
@PreUpdate
public void PreUpdate(Object entity){
log.info("开始更新--"+entity.toString());
}
/**
* 被@Postpersist注解的方法 ,完成save之后的操作。
* @param entity
*/
@PostPersist
public void PostPersist(Object entity){
log.info("结束保存--"+entity.toString());
}
/**
* 被@Postupdate注解的方法 ,完成update之后的操作。
* @param entity
*/
@PostUpdate
public void PostUpdate(Object entity){
log.info("结束更新--"+entity.toString());
}
}
2) 实体类定义:
@Data
@javax.persistence.Table(name="sys_role")
@Table(appliesTo="sys_role",comment = "角色表") //此注解主要为了添加表注释
@Entity
@EntityListeners(value = {TestEntityListeners.class})
public class Role implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, columnDefinition = "varchar(63) comment '角色名称'")
private String roleName;
@Column( columnDefinition = "varchar(63) comment '角色描述'")
private String description;
@Column( columnDefinition = "bit comment '是否启用'")
private Boolean enabled;
}
3) 测试
@Test
public void updateRole(){
Role role=new Role();
role.setId(4L);
role.setRoleName("管理员");
roleRepository.saveAndFlush(role);
}
4) 结果:
Hibernate: select role0_.id as id1_1_0_, role0_.description as descript2_1_0_, role0_.enabled as enabled3_1_0_, role0_.role_name as role_nam4_1_0_ from sys_role role0_ where role0_.id=?
2019-05-31 09:28:29.242 INFO 15548 --- [ main] c.x.a.repository.TestEntityListeners : 开始更新--Role(id=4, roleName=管理员, description=null, enabled=null)
Hibernate: update sys_role set description=?, enabled=?, role_name=? where id=?
2019-05-31 09:28:29.273 INFO 15548 --- [ main] c.x.a.repository.TestEntityListeners : 结束更新--Role(id=4, roleName=管理员, description=null, enabled=null)
在方法中利用反射机制,可以实现对(创建日期,创建者,更新日期更新者,删除日期,删除者)等注解的字段的赋值操作。
3. @Embedded和@Embeddable注解
当一个实体类要在多个不同的实体类中进行使用,而本身又不需要独立生成一个数据库表,这就是需要使用@Embedded、@Embeddable
Address里加上了@Embeddable这个注解表示,Address这个类是一个可以被嵌套的类,而在Author类中,我们声明了一个Address类型的变量address,然后给它加上@Embedded注解,意思是我们要在Author类嵌套Address类。
当被引用的对象和主对象拥有相同的生命周期的时候才考虑使用@Embedded和@Embeddable。简单的说就是Author类存在的时候才会有Address类,当Author类不存在的时候,对应Author类所以诞生的Address类也应该是不存在的。通俗的说就是作者存在的时候才会有这个作者的地址。而不会是有一个地址存在着却没有人属于这个地址。而且内嵌类会和主类生成一张表,所以内嵌类对应主类应该是要唯一的和拥有相同生命周期的。
@Embedded 用来修饰 对象属性(引用类型 -- 类对象 -- 属性注解)
@Embeddable 用来修饰 类(类注解)
Address .java
@Data
public class Address implements Serializable{
private String country;
private String province;
private String city;
private String detail;
}
Person.java
@Entity
public class Person implements Serializable{
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer age;
private Address address;
}
1) 两个注解全不使用
那么两个实体类和上面的相同,Address属性字段会映射成tinyblob类型的字段,这是用来存储不超过255字符的二进制字符串的数据类型,显然我们通常不会这么使用。
2) 只使用@Embeddable
在Address实体类上加上@Embeddable注解,变成如下类:
@Embeddable
@Data
public class Address implements Serializable{
private String country;
private String province;
private String city;
private String detail;
}
而Person实体类不变,把Address中的字段映射成数据列嵌入到Person表中了。
@Embeddable
@Data
public class Address implements Serializable{
@Column(nullable = false)
private String country;
@Column(length = 30)
private String province;
@Column(unique = true)
private String city;
@Column(length = 50)
private String detail;
}
在Address中配置的属性全部成功映射到Person表中。
只使用@Embedded和只使用@Embeddable产生的效果是相同的。
覆盖@Embeddable类中字段的列属性:
这里就要使用另外的两个注解@AttributeOverrides和@AttributeOverride,这两个注解是用来覆盖@Embeddable类中字段的属性的。
@AttributeOverrides:里面只包含了@AttributeOverride类型数组;
@AttributeOverride:包含要覆盖的@Embeddable类中字段名name和新增的@Column字段的属性;
Person类:
@Data
@Entity
public class Person implements Serializable{
private static final long serialVersionUID = 8849870114127659929L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer age;
@Embedded
@AttributeOverrides({@AttributeOverride(name="country", column=@Column(name = "person_country", length = 25, nullable = false)),
@AttributeOverride(name="city", column = @Column(name = "person_city", length = 15))})
private Address address;
}
Address类:
@Embeddable
@Data
public class Address implements Serializable{
@Column(nullable = false)
private String country;
@Column(length = 30)
private String province;
@Column(unique = true)
private String city;
@Column(length = 50)
private String detail;
}
4. @DynamicInsert和@DynamicUpdate
动态更新与插入:
@DynamicInsert属性:设置为true,设置为true,表示insert对象的时候,生成动态的insert语句,如果这个字段的值是null就不会加入到insert语句当中.默认false。
比如希望数据库插入日期或时间戳字段时,在对象字段为空的情况下,表字段能自动填写当前的sysdate。
@DynamicUpdate属性:设置为true,设置为true,表示update对象的时候,生成动态的update语句,如果这个字段的值是null就不会被加入到update语句中,默认false。
比如只想更新某个属性,但是却把整个对象的属性都更新了,这并不是我们希望的结果,我们希望的结果是:我更改了哪些字段,只要更新我修改的字段就够了。
1) 实体类:
默认值为:true
@DynamicUpdate(false)
@Data
@javax.persistence.Table(name="sys_role")
@Table(appliesTo="sys_role",comment = "角色表") //此注解主要为了添加表注释
@Entity
@DynamicUpdate(true)
public class Role extends BaseEntity implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, columnDefinition = "varchar(63) comment '角色名称'")
private String roleName;
@Column( columnDefinition = "varchar(63) comment '角色描述'")
private String description;
@Column( columnDefinition = "bit comment '是否启用'")
private Boolean enabled;
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
@Column(columnDefinition = "datetime comment '创建时间'")
protected Date createTime;
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(columnDefinition = "datetime comment '更新时间'")
protected Date updateTime;
@Column(columnDefinition = "bit comment '逻辑删除'")
protected Boolean deleted;
}
2) 测试:
@Test
public void updateRole(){
Role role=new Role();
role.setId(4L);
role.setRoleName("管理员");
roleRepository.saveAndFlush(role);
}
3) 结果:
Hibernate: select role0_.id as id1_1_0_, role0_.create_time as create_t2_1_0_, role0_.deleted as deleted3_1_0_, role0_.update_time as update_t4_1_0_, role0_.description as descript5_1_0_, role0_.enabled as enabled6_1_0_, role0_.role_name as role_nam7_1_0_ from sys_role role0_ where role0_.id=?
Hibernate: update sys_role set create_time=?, update_time=? where id=?
因为数据表中的create_time有值,所set后面会有create_time字段。
一定要注意,如果要保留该值,一定要传入该参数。
说明:
@DynamicUpdate 如果为空,则不会生成sql语句,不更新,如果内容不为空,没有设置值,则会生成语句,更新为空。