Jpa 注解详解 映射详解 一对多 多对一

发布日期:2019-06-10

Jpa映射详解

该博客例子均用 SpringBoot + Spring Data Jpa 实现

一、常用注解

这里主要介绍了最常用注解,实现POJO和数据库的隐射。

@Entity

对类注释。任何Hibernate映射对象都要有这个注释持久层将对象映射到数据库,JPA是一种规范,Hibernate是一种实现,可以将POJO映射到数据库。这种类就叫Entity Bean

@Table

对类注释。通过它可以为实体指定表(talbe),目录(Catalog)和schema的名字

@ID

声明该属性为主键,指定类的主键

@GeneratedValue

指定主建的声明策略,自动生成主键

TABLE:数据库产生主键IDENTITY:数据库自增长SEQUENCR :通过数据库的序列产生主键

AUTO:默认选项

@Column

声明该属性与数据库字段的映射关系,Hibernate会自动匹配属性和字段名字相同的,@Column可以修改隐射值

@Transitent

若属性非数据库字段,则可以声明该属性与数据库字段的不产生映射关系

参考用例

# 配置 Hibernate 自动生成表spring.jpa.properties.hibernate.hbm2ddl.auto=update

定义两个实体表,Author和Poetry,就将刚才所有的注解配置上,启动服务springboot 扫描注解@Entity后,会自动生成数据库表,最后我们查看下数据库表中的结构就知道答案了

Author

@Entity@Table(name = "authorCN")public class Author implements Serializable { private static final long serialVersionUID = 4074367019988114836L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "p_name") private String name; @Column(name = "p_age") private Integer age; @Transient private String error; // getter and setter }

Poetry

@Entity@Tablepublic class Poetry implements Serializable { private static final long serialVersionUID = -2743230230981482358L; @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Integer id; private String title; private String summary; private String content; // getter and setter }

查看数据库表,可以看到Poetry的字段,表明都是和属性一样的,而Author里面加了@Column注解的属性后,生成的字段名发生的改变,并且表名也有改变,@Transient下的属性并没有生成数据库表中的字段。

通过测试启动服务插入数据,我们可以查看主键的生成策略

@Test public void testSavaData() { authorRepository.save(new Author("杜甫", 56)); authorRepository.save(new Author("李白", 42)); poetryRepository.save(new Poetry("八阵图", "咏怀诗", "功盖三分国,名高八阵图。江流石不转,遗恨失吞吴。")); poetryRepository.save(new Poetry("春望", "爱国诗", "国破山河在,城春草木深。感时花溅泪,恨别鸟惊心。烽火连三月,家书抵万金。白头搔更短,浑欲不胜簪。")); poetryRepository.save(new Poetry("静夜思", "思乡诗", "床前明月光,疑是地上霜。举头望明月,低头思故乡")); }

我们查看日志可以得知,因为在Poetry里面设置的生成策略是SEQUENCE,所以,在每次插入数据的时候,都有hibernate_sequence的更新。

Hibernate: insert into authorcn (p_age, p_name) values (?, ?)Hibernate: insert into authorcn (p_age, p_name) values (?, ?)Hibernate: select next_val as id_val from hibernate_sequence for updateHibernate: update hibernate_sequence set next_val= ? where next_val=?Hibernate: insert into poetry (content, summary, title, id) values (?, ?, ?, ?)Hibernate: select next_val as id_val from hibernate_sequence for updateHibernate: update hibernate_sequence set next_val= ? where next_val=?Hibernate: insert into poetry (content, summary, title, id) values (?, ?, ?, ?)Hibernate: select next_val as id_val from hibernate_sequence for updateHibernate: update hibernate_sequence set next_val= ? where next_val=?Hibernate: insert into poetry (content, summary, title, id) values (?, ?, ?, ?)

二、POJO之间的关系

(1)一对一关系

因为之前古诗和作者是多对一关系,所以呢,这里我要将一对一,我就创建了妻子的表,夫妻关系一对一,哈哈,古代我们也是一夫一妻制

在这之前,我偷偷的在数据库加了些测试数据,这个就不列出来了

@Entity@Tablepublic class Wife implements Serializable { private static final long serialVersionUID = 381807136517697285L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String wname; private Integer wage; // getter and setter

在Author声明属性wife,指定对应关系

@OneToOne(cascade = CascadeType.ALL)@JoinColumn(name = "wife_id")private Wife wife;

@OneToOne

顾名思义,就是一对一关系,cascade是级联操作,CascadeType有多种配置

ALL 所有PERSIST 级联持久化(保存)操作MERGE 级联更新(合并)操作REMOVE 移除REFRESH 刷新DETACH 脱管/游离操作

@JoinColumn

保存表与表之间关系的字段,同@Column,name只指定数据库字段,wife_id,但是默认指向该表的主键,如何要让wife表其他字段做外建,就需要使用referencedColumnName指明对应表的字段。我这里没有明确举例,其他小伙伴可以自己去尝试。

@JoinColumn(name = "wife_id",referencedColumnName="name")

测试:建立Controller类,启动服务访问

@RestController@RequestMapping("/jpa")public class IndexController { @Autowired AuthorRepository authorRepository; @Autowired PoetryRepository poetryRepository; @Autowired WifeRepository wifeRepository; @GetMapping("/author/{id}") public Author findAuthorById(@PathVariable(value = "id") Integer id){ return authorRepository.findById(id).get(); }}

这里@Controller效果如图:

当然这里只是一对一的单向关联,主导权全在author那边,而wife这边没有任何可以修改的权限,对于这种也是可以有解决的办法的,我们建立双向关联,

在wife类里添加

@OneToOne(mappedBy="wife")@JsonBackReferenceprivate Author author;

mappedBy指的被映射,如何要双向关联,必须要指定mappedBy,而@JsonBackReference注解是因为双向关联后,你中有我,我中有你,就会无限循环下去,所以在任何一方添加 @JsonBackReference可以解决这个问题,通过debug我们可以看到如果不加 @JsonBackReference的后果

(2)一对多关系

这里一对多关系,我们就用之前的作者表(author)和古诗(Poetry),李白一个人就有一千多首诗呢

好了,还是先单向关联,主动权肯定在author 作者这边。

在Author加入

@OneToManyprivate List<Poetry> poetryList;

这里就有点不同了,一对一中外键关系wife_id主动权在author手中,那么数据表这边就会添加一个字段wife_id,但是一对多就不同了,主键但是对应多个,不可能生成外键,所以外键是放在多那边,就是古诗表(poetry);

Poetry

多对一里面是主动的,@JoinColumn指定外键authorCN_id

@ManyToOne@JoinColumn(name = "authorCN_id")@JsonBackReferenceprivate Author author;

Author

一对多里面是被动的,mappedBy指向Poetry里面的author

@OneToMany(mappedBy = "author")private List<Poetry> poetryList;

建立后,查询结果如下,可以看到关联到杜甫的诗就都出来了

(3)多对多关系

多对多的话,就拿读者和古诗举例吧,读者可以读很多古诗,古诗也可以有不同的读者

@Entity@Tablepublic class Reader implements Serializable { private static final long serialVersionUID = 4074367019988114836L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private Integer age; @ManyToMany @JoinTable(name = "PoetryAndReader", joinColumns ={@JoinColumn(name = "reader_id")}, inverseJoinColumns = {@JoinColumn(name = "poetry_id")}) private List<Poetry> poetryList;}

@ManyToMany

多对多的注解

@JoinTable

因为多对多不存在直接外键外键,所以建立第三方表里面存储双方法的主键作为外键,name就是第三方表的名字,joinColumns就是配置连接表中外键列的信息,该属性值可接受多个@JoinColumn,用于配置连接表中外键列的信息

结束

里面的代码只粘贴了重要部分的,如果哪个小伙伴要跑一下看下具体效果,我把代码贴到了GitHub里面,有兴趣的可以去copy.

https://github.com/AugusDuan/jpaTest