3.1 JPA 多表查询
多表查询在 Spring Data JPA 中有两种实现方式,第一种是创建一个结果集的接口来接受多表连接查询后的结果,第二种是利用 JPA 的关联映射来实现
3.1.1 数据库表及关系
CRM 数据库中除 sys_user(用户)表外,还包括sys_role(角色)表。
sys_role(角色)表脚本:
CREATE TABLE `sys_role` (
`role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`role_name` varchar(50) DEFAULT NULL COMMENT '角色名称',
`role_desc` varchar(50) DEFAULT NULL COMMENT '角色描述',
`role_flag` int(11) DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`role_id`),
UNIQUE KEY `role_id` (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8
sys_user(用户)表与 sys_role(角色)表之间存在着主外键关系, sys_user(用户)表中的usr_role_id外键字段对应sys_role(角色)表中的主键 role_id 字段。
3.1.2 多表联接查询
1.在 com.bdqn.crm.entity 包下创建Role实体类
@Entity
@Table(name = "sys_role")
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private long roleId;
@Column(name = "role_name")
private String roleName;
@Column(name = "role_desc")
private String roleDesc;
@Column(name = "role_flag")
private long roleFlag;
2.在 com.bdqn.crm.vo 包下创建 UserInfo 接口,里面提供所需数据的getter方法,其中包括用户数据和角色名称(roleName)。
public interface UserInfo {
Long getUsrId();
String getUsrName();
String getUsrPassword();
Long getUsrRoleId();
Integer getUsrFlag();
// 角色名称
String getRoleName();
}
- 在运行中Spring会给接口(UserInfo)自动生产一个代理类来接收返回的结果,代码中使用getXX的形式来获取。
3.在UserRepository中添加查询方法,返回类型设置UserInfo:
@Query("select u.usrId as usrId, u.usrName as usrName, u.usrPassword as usrPassword,"
+ "u.role.roleId as usrRoleId, u.usrFlag as usrFlag, r.roleName as roleName "
+ "from User u, Role r where u.role.roleId=r.roleId and u.usrId=?1")
public UserInfo getUserInfo(Long usrId);
4.测试验证:
@Test
public void testGetUserInfo(){//测试多表联接查询
UserInfo userInfo = userRepository.getUserInfo(2L);
System.out.println("usrName:" + userInfo.getUsrName());
System.out.println("roleName:" + userInfo.getRoleName());
}
3.1.3 关联映射
具体例子来介绍如何映射以下关联关系:
- 以 User 和 Role 为例,介绍如何映射多对一单项关联映射
- 以 Role 和 User 为例,介绍如何映射一对多双向关联映射
3.1.3.1 单向多对一关联
首先以 User 和 Role 为例,介绍通过使用外键来建立多对一的单向关联关系。
1.修改 User 实体,添加关联 Role 对象:
@Entity
@Table(name = "sys_user")
//@NamedQueries(@NamedQuery(name = "User.findUsersByName",query = "select u from User u where u.usrName = ?1"))
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "usr_id")
private Long usrId;
@Column(name = "usr_name")
private String usrName;
@Column(name = "usr_password")
private String usrPassword;
@ManyToOne(targetEntity = Role.class)
@JoinColumn(name = "usr_role_id")
private Role role;
@Column(name = "usr_flag")
private Integer usrFlag;
- @ManyToOne 注解映射多对一关联关系,targetEntity 属性表示关联实体类型,可省略;
- @JoinColumn 注解映射关联的外键字段,如不指定,则生成一张新表维护两个对象之间的关系;
2.创建 UserRepository
public interface UserRepository extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {
}
3.测试基本的CRUD
@Test
public void testGet(){//测试按主键查询用户,并输出关联的用户数量
Role role = roleRepository.findById(1L).get();
System.out.println("roleName:" + role.getRoleName());
System.out.println("users.size:" + role.getUsers().size());
}
3.1.3.2 双向一对多关联
在前面的实例中,已经建立了 User 类到 Role 类的单向多对一关联,下面再继续通过示例完成Role 类到 User 类的双向一对多关联。
1.修改 Role 实体类,添加关联的 User 对象集合:
@Entity
@Table(name = "sys_role")
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private long roleId;
@Column(name = "role_name")
private String roleName;
@Column(name = "role_desc")
private String roleDesc;
@Column(name = "role_flag")
private long roleFlag;
@OneToMany(targetEntity = User.class,fetch = FetchType.EAGER,cascade = CascadeType.PERSIST,mappedBy = "role")
private Set<User> users = new HashSet<User>();
// 省略其他代码
}
- targetEntity 属性表示关联的实体类
- fetch 属性表示加载策略,FetchType 取值有LAZY 及 EAGER ,LAZY 表示延迟加载,EAGER 表示立即加载,@ManyToOne 注解也包含该属性,且默认值为EAGER,表示立即加载,所以查询 User 时通过左外连接立即获取Role 数据;@OneToMany 注解表示该属性默认值为 LAZY。
- cascade 属性表示级联操作,CascadeType 取值有 PERSIST、REMOVE、ALL ......等。
- mappedBy 属性用来设置对象之间的关系维护方
2.创建RoleRepositoty
public interface RoleRepository extends JpaRepository<Role,Long> {
}
3.测试查询
@Test
public void testGet(){//测试按主键查询用户,并输出关联的用户数量
Role role = roleRepository.findById(1L).get();
System.out.println("roleName:" + role.getRoleName());
System.out.println("users.size:" + role.getUsers().size());
}
4.测试级联操作:级联新增
@Test
public void testAdd(){ // 测试级联新增
Role role = new Role("测试角色","演示级联新增角色和用户",1);
User user1 = new User("测试角色1","123456",role,1);
User user2 = new User("测试角色2", "123456",role,1);
// 将 User 添加到 Role 的users 集合中,进行级联新增
role.getUsers().add(user1);
role.getUsers().add(user2);
roleRepository.save((role)); // 新增角色的同时新增关联的用户
}
5.测试级联操作:级联删除
将级联属性的值改为REMOVE,再来测试级联删除,将刚刚创建的 Role 对象及关联的 User 对象一次删除。
@Test
public void testDelete(){ // 测试级联删除
// 先使用 getOne 方法获取到 Role 的应用,然后调用 delete 方法删除
Role role = roleRepository.getOne(33L);
userRepository.deleteByRoleId(role.getRoleId());
roleRepository.delete(role);
}
3.2 Spring Boot 集成 MyBaties
优点:
- SQL 被统一提取出来,便于统一管理和优化
- SQL 和代码解耦,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护、更易单元测试
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射
- 提供对象关系映射标签,支持对象关系组件维护
- 灵活书写动态 SQL,支持各种条件来动态生成不同的 SQL
缺点:
- 编写 SQL 语句时工作量很大,尤其是字段多、关联表多时,更是如此
- SQL 语句依赖于数据库,导致数据库移植性差
3.2.2.2 xml 配置版集成
XML 版本保持映射文件的方式,且最佳的开发方式不需要实现 Dao 的实现层,系统会自动根据方法名在映射文件中找到对应的SQL。
接下来给大家介绍一下如何使用XML 配置版集成 Spring Boot。
1.创建项目:
2.添加关键依赖包:
任何使用方式都需要首先在 pox.xml 中引入 mybatis-spring-boot-starter 的启动器,我们使用项目向导添加的依赖如下:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
3.application.properties 配置相关信息:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///crm?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=liuyuhan
mybatis.config-location=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
mybatis.type-aliases-package=com.bdqn.crm.pojo
5.启动类:
在启动类中添加对Mapper包扫描@MapperScan,Spring Boot 启动的时候会自动加载包路径下的 Mapper。
@SpringBootApplication
@MapperScan("com.bdqn.crm.mapper")
public class MybatiesXmlApplication {
public static void main(String[] args) {
SpringApplication.run(MybatiesXmlApplication.class, args);
}
}
6.编码:
resource/mybatis 目录下添加mybatis-config.xml,配置一些全局属性:
<!-- 全局配置文件 -->
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 开启控制台日志 -->
<!-- <setting name="logImpl" value="STDOUT_LOGGING" />-->
<setting name="lazyLoadingEnabled" value="true" />
<!--全自动映射级别-->
<setting name="autoMappingBehavior" value="FULL" />
</settings>
编写实体类 User.java:
public class User implements Serializable {
private long usrId;
private String usrName;
private String usrPassword;
private long usrRoleId;
private long usrFlag;
// 省略 setter&getter
}
编写 Mapper 接口: