聊聊Spring Data JPA的注解

阅读本文之前,为了更好的理解,你应该先搭建一个Spring Data JPA的helloword的例子

概念

在说Spring Data JPA(后面简称SDJ)之前,先了解下什么是JPA(java persistence api)。

  • JPA是一种ORM(对象关系映射,Object Relational Mapping,描述对象与数据库映射之间的元数据)规范,JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问 API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
  • hibernate 这种框架实现了这种规范(不仅这一个框架),SDJ 对hibernate做了一层封装。SDJ 和Mybatis理念上最大的区别就是前者是面向对象的,mybatis是面向关系的。

何为JPA

  • JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
  • JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

JPA的规范

JPA规范的对象以及如何规范。通过一些列的注解来标注普通实体对象,以及对象的之间的关系,使指成为Domain Class(域对象)。在此对象封装一层操作API。

  • 实体注解
  1. @Entity 注解在实体类上,也是JPA的操作对象。
  2. @Table 指定实体类对应的表名。
  3. @Id 指定主键。
  4. @GeneratedValue 主键的生成策略。可选的类型有
    TABLE:使用一个特定的数据库表格来保存主键。一般配合@TableGenerator使用
    SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列,比如oracle。 一般配合@SequenceGenerator使用
    IDENTITY:主键由数据库自动生成(主要是自动增长型)
    AUTO:主键由程序控制。
  5. @Column 可以对实体类的属性起别名,对应表中的字段,以及对该字段添加一些非空等约束。

  • 关系注解
    在介绍关系的注解之前,先了解一个概念,关系拥有者(owning side,后面简称ow)和非拥有者(non-owning sise,后面简称now)。下面的例子中,User对象称之为关系拥有者,Heart对象称之为非拥有者。

例子1 User和Heart对象

public class User{
  //主键
  private Long id;
  //心脏
  //@OneToOne
  //@ManyToOne
  private Hear heart;
  //...
}

public class Heart{
  //主键
  private Long id;
  //重量
  private Double weight;
  //...
}
  1. @OneToOne 表示实体类与嵌入式类之间一对一的映射关系。这个注解用在一个实体类包含一个嵌入式的类。比如在在例子1里,这个注解放在User类的Heart属性上。当SDJ生成表的时候,会在User表上生成一个字段名为heart_id的外键。该注解有个级联cascade的属性,可选(ALL,PERSIST,MERGE,REMOVE,REFRESH,DETACH),拿PERSIST说明,当调用Spring Data JPA中的Reponsitory中的save方法保存User对象的时候,如果User对象中含有Heart对象的实例时候,会同时将Heart对象也保存到数据库中。

  2. @ManyToOne、@OneToMany、@ManyToMany 用法都差不多。具体细节。后面会再次说明


  1. @JoinColumn 此注解一般会配合上面的6、7两点中的关系注解一起使用。表示关系拥有者的外键相关。
  • 当此注解和@OneToOne或者@ManyToOne注解一起使用的时候,那么@Join注解 的属性值就表示外键的名字,并且此外键会在该注解所在实体类对应的表中。在例子2中,利用SDJ自生成表,会在User表中生成一个heart_id字段的外键,该外键对应的是Heart表中的主键。除非你想指定外键的名字,否者这种情况下@JoinColumn可以完全去掉

例子2

public class User{
  //主键
  private Long id;
  //心脏
  @OneToOne
  @ManyToOne(表示多个人对应一个心脏,比如连体人,例子不是那么恰当,大致是这个意思)
  @JoinColumn(name = "heartId",referencedColumnName = "id")
  
  private Hear heart;
  //...
}

public class Heart{
  //主键
  private Long id;
  //重量
  private Double weight;
  //...
}
  • @JoinColumn和@OneToMany使用的时候,并且是单向关系(关于单向/双向的解释,随后举例)的时候,那么@JoinColumn注解中的name就表示ow类对应表的外键名,referencedColumnName表示外键关联ow类对应的表。

例子3


public class Country{
  //主键
  private Long id;
  
  @OneToMany
  @JoinColumn(name = "countryId",referencedColumnName = "id")
  
  private List<People> peoples;
  //...
}

public class People{
  //主键
  private Long id;
  //重量
  private String name;
  //...
}

在这个例子里,people对应的表会有个外键,对应着country实体的表的id。

到目前为止,说的都是实体类的单向关系(unidirectional),比如拿上面的例子来说,通过country类 我们知道有哪些people,但是从people类,我并不知道people对应的country。此时若想满足这个需求,就得靠注解形成双向关系(bidirectional)。请看下面的例子。

例子4


public class Country{
  //主键
  private Long id;
  
  @OneToMany(mappedBy="country")
  private List<People> peoples;
  //...
}

public class People{
  //主键
  private Long id;
  //
  private String name;

  @ManyToOne
  private Country country;
  //...
}

可以看出在双向关系中,需要在相互关系的类中配置对应的注解。此时表结构与例子3的表结构一样,在例子4中,@OneToMany的mappedBy注解是只能配置在双向关系中。

  1. @ManyToMany 多对多映射,这种关系肯定会设计到第三张表来存储实体之间的关系。

例子5

public class User{
  private Long id;
  private String name;
  @ManyToMany
  private List<Role> roleList;
}

public class Role{
  private Long id;
  private String name;
}

此时你如果用SDJ自动生成表的话,你会在数据库里面看到一张除了User和Role之外的表。如下

create table if not exists jpa.user_role_list
(
    user_id bigint not null,
    role_id bigint not null,
    constraint FK8hyj0c7rglkeyinrekdeul39
        foreign key (user_id) references jpa.user (id),
    constraint FKofayuingjc8gnn4bkdek0s8sv
        foreign key (role_id) references jpa.role (id)
);

你可以指定生成的表名以及里面的列名,对User修改如下

public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false,length = 12)
    private  String name;

    private Integer sex;

    @ManyToMany
    @JoinTable(name = "t_user_role",joinColumns = {@JoinColumn(name = "userId",referencedColumnName = "id")}
    ,inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "id")})
    private List<Role> roleList;

此时生成的关联表结构如下

create table if not exists jpa.t_user_role
(
    user_id bigint not null,
    role_id bigint not null,
    constraint FKjwwimwt7mcivvbosl7rms6q4y
        foreign key (role_id) references jpa.role (id),
    constraint FKm61t2kt35dkh35mbd8t0wjisl
        foreign key (user_id) references jpa.user (id)
);

小结

到目前为止,了解了JPA中这些主要的注解,主要是通过注解来标注实体类(domain class)与表的映射关系以及实体类之间的关系,当然实际也是表之间的关系。回过头看,如果我们的系统中 ORM框架切换到mybatis这种,可能在项目的前期工作转备好之后,我们先设计表,甚至现在都不会建立外键这种约束了,而是会完全由业务代码来维护。而JPA主要让使用者只关心domain class,表之间的关系也不需要使用者去在意,所谓的面向OO,而mybatis这种是面向关系的。听上去OO编程显得更高大上一点,毕竟java就是OO的语言。

下一节 说说Spring Data JPA的使用。

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

推荐阅读更多精彩内容