系列文章:
spring-data-jpa 入门
spring-data-jpa 入门三:常用技术使用之复杂查询
在上文中我们介绍了spring-data-jpa简单的实现原理、简单的增删改查、以及简单的分页查找、排序。基本上通过上文介绍,我们可以写一些简单的增删改查了!但是日常开发中肯定不是仅仅单表查询。那么我们将继续探讨spring-data-jpa一些其他使用放法:
说道orm框架基本上离不开多表关联关系查询,例如:一对一,一对多,多对多。无论在mybatis或者hibernate中都是支持注解或者配置查询的,jpa也不例外,本文我们将着重说下spring-data-jpa关联关系查询,已经我在配置中碰到的一些坑。
有两张表 user、user_details,用户信息表和用户详情表是一对一的关系,用户详情表关联了用户信息表的ID,并配置了外键,具体表结构如下:
CREATE TABLE `user_info` (
`id` int(11) NOT NULL auto_increment,
`uname` varchar(20) default NULL,
`unumber` varchar(20) default NULL,
`password` varchar(255) default NULL,
`address` varchar(255) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_details` (
`id` int(11) NOT NULL auto_increment,
`user_id` int(11) NOT NULL,
`email` varchar(50) default NULL,
`address` varchar(255) default NULL,
PRIMARY KEY (`id`),
KEY `FKbp1gkvqqbmw0a9j4w53c9q5gm` (`user_id`),
CONSTRAINT `FKbp1gkvqqbmw0a9j4w53c9q5gm` FOREIGN KEY (`user_id`) REFERENCES `user_info` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户详情表';
Entity层
为了节省篇幅我就不把全部代码贴进来了,只贴关键代码
UserDetails中:
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id", referencedColumnName = "id")
private UserInfo userInfo;
在UserDetails实体中除了正常的结构属性外,我们添加了一个UserInfo对象,并使用了注解@OneToOne,其中cascade(串联、级联)配置了级联属性为所有,当然对应的还有保存、更新、删除、刷新、分离等
package javax.persistence;
public enum CascadeType {
ALL,
PERSIST,
MERGE,
REMOVE,
REFRESH,
DETACH;
private CascadeType() {
}
}
@JoinColumn 设置关联对应的字段,referencedColumnName 是设置关联表的具体某个字段,如果关联字段外键为主键的话这个属性可以忽略,整体这句话就是在说根据UserDetails中的user_id(外键),是与UserInfo 中的id(主键)是一一对应的。
UserInfo中:
@JsonIgnoreProperties(value = "userInfo")//避免递归死循环
@OneToOne(mappedBy = "userInfo", cascade = CascadeType.ALL)
private UserDetails userDetails;
@OneToOne不用多说,mappedBy 这个属性就是表示该实体是在整个一对一关系中属于被维护端,想对应的userDetails是处于维护端(维护一对一的关系)。
@JsonIgnoreProperties(value = “userInfo”) 这个注解就是在一对一查询的时候出现递归死循环的,例如 我查询用户信息时候会自动加载用户详情信息,但是尴尬的是用户详情信息中也有用户信息,如此这般就成了递归死循环,报错StackOverflowError。
到这一对一关系配置就搞定了,查询用户信息的时候会执行查询对应的用户详情信息,我们配置的是双向查询,如果要单项的将对应属性注释掉就行。
一对多和多对一关系演示我们用 用户(作者)、文章。一个作者可以发表多篇文章,一篇文章只能有一个作者。
用户表结构和一对一的用户信息表一样,文章表结构如下
CREATE TABLE `article` (
`id` int(11) NOT NULL auto_increment,
`user_id` int(11) default NULL,
`context` longtext,
`create_time` datetime default NULL,
`update_time` datetime default NULL,
PRIMARY KEY (`id`),
KEY `FKgfkys9w7qv3xcubq0drrayuu3` (`user_id`),
CONSTRAINT `fore_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_info` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文章信息表';
ArticleInfo 实体代码关键配置如下:
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH},
optional = false,fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", referencedColumnName = "id")
private UserInfo userInfo;
@ManyToOne 表示这是个多对一的关系,cascade对应的级联操作,optional(任选)表示是否接受为空,false不接受。fetch( 取来) 加载模式,可选的有懒加载、立即加载。配置懒加载之后会有一定问题,需要另行配置,方案大概有四种,我选择的是在web.xml中配置过滤器。
<filter>
<filter-name>OpenEntityManagerInViewFilterfilter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilterfilter-class>
filter>
<filter-mapping>
<filter-name>OpenEntityManagerInViewFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
具体原因以及解决方法后面在解释。
@JoinColumn 同一对一解释。
对应的UserInfo实体中也需要添加配置:
@JsonIgnoreProperties(value = "userInfo")//避免递归死循环
@OneToMany(mappedBy = "userInfo", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List articleInfoList;
注意了因为用户是一 所以用@OneToMany注解,统同样的mappedBy表示在一对多的关系中,它是关系被维护着。
这样一对多双向关联就配置完毕,查询会将对应关系的实体也查出来。
多对多的关系的演示选择的是 用户、权限。 一个用户可用有多个权限,一个权限对应多个用户。
用户表结构并没有变,权限表结构如下:
CREATE TABLE `role` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(25) NOT NULL,
`create_time` datetime default NULL,
`update_time` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='权限表';
可用看出权限表并没有与用户表有任何关联的地方,这是因为在JPA中规定@ManyToMany表示多对多的关系,维护与被维护关系给谁都行,谁是主表谁的表名在前,并且使用一个第三方的关联表来维护。关联关系表不需要我们建立,只需要使用@JoinTabl注解,jpa会自动根据规则进行关联表的生成。
关联关系的规则
表名默认是:主表名+下划线+从表名。(主表是指关系维护端对应的表,从表指关系被维护端对应的表)。这个关联表只有两个外键字段,分别指向主表ID和从表ID。
字段的名称默认为:主表名+下划线+主表中的主键列名,从表名+下划线+从表中的主键列名
对应用户实体实体配置:
@JsonIgnoreProperties(value = "userInfoList")//避免递归死循环
@ManyToMany
@JoinTable(name = "user_info_role",
joinColumns = {@JoinColumn(name = "user_info_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
private List roleList;
@JsonIgnorePropertie 避免递归死循环
@ManyToMany 表示的是多对多的关系
@JoinTable 就是上面我们说的生成第三方关联关系表,其中name就是表名,joinColumns、inverseJoinColumns都是配置对应的列名,这个关联关系表中只有两列(上面生成规则说明)。
对应的权限实体关键配置如下:
@ManyToMany(mappedBy = "roleList")
private List userInfoList;
这个配置就更简单了,到这一对多配置就搞定了,这里需要注意的是:
其实jpa本身就是开发方便,所有在这种关联关系配置中,非常简单。当然,中间在配置懒加载时候还是出现了点问题的。
到此,我们通过两篇文章的说明了解到spring-data-jpa是什么、简单的实现原理、简单的增删改查、以及简单的分页查找、排序、常用技术中的关联关系配置(一对一、一对多、多对多),下文我们将继续探讨: