Spring Data JPA--注解使用(一)

一、基本实体注解

文档地址: 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语句,不更新,如果内容不为空,没有设置值,则会生成语句,更新为空。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容