一、 Spring Data JPA 介绍
Spring Data:其实 Spring Data 就是 spring 提供了一个操作数据的框架。而 Spring Data JPA
只是 Spring Data 框架下的一个基于 JPA 标准操作数据的模块。
Spring Data JPA:基于 JPA 的标准对数据进行操作。简化操作持久层的代码。只需要编
写接口就可以。
二、 Spring Boot 整合 Spring Data JPA
1 搭建整合环境
2 修改 POM 文件添加坐标
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>22-spring-boot-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.7</java.version>
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>
2.0.4
</thymeleaf-layout-dialect.version>
</properties>
<dependencies>
<!-- springBoot 的启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springBoot 的启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- springBoot 的启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>
3 在项目中添加 application.properties 文件
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
4 添加实体类
@Entity @Table(name="t_users")
public class Users {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="id")
private Integer id;
@Column(name="name")
private String name;
@Column(name="age")
private Integer age;
@Column(name="address")
private String address;
...
}
5 编写 Dao 接口
/**
* 参数一 T :当前需要映射的实体
* 参数二 ID :当前映射的实体中的 OID 的类型
*
*/
public interface UsersRepository extends JpaRepository<Users,Integer> { }
6 在 pom 文件中添加测试启动器的坐标
<!-- 测试工具的启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
7 创建启动类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
8 编写测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes=App.class)
public class UsersRepositoryTest {
@Autowiredprivate UsersRepository usersRepository;
@Test public void testSave(){
Users users = new Users();
users.setAddress("北京市海淀");
users.setAge(20);
users.setName("张三");
this.usersRepository.save(users);
}
}
三、 Spring Data JPA 提供的核心接口
1 Repository 接口
2 CrudRepository 接口
3 PagingAndSortingRepository 接口
4 JpaRepository 接口
5 JPASpecificationExecutor 接口
四、 Repository 接口的使用
1 提供了方法名称命名查询方式
2 提供了基于@Query 注解查询与更新
1 方法名称命名查询方式
1.1编写接口
/**
* Repository 接口的方法名称命名查询*
*
*/
public interface UsersRepositoryByName extends Repository<Users, Integer> {
List<Users> findByName(String name);
List<Users> findByNameAndAge(String name,Integer age);
List<Users> findByNameLike(String name);
}
方法名称命名查询方式的规则:
//方法的名称必须要遵循驼峰式命名规则。findBy(关键字)+属性名称(首字母要大写)+查询条件(首字母大写)
1.2测试代码
/**
* Repository--方法名称命名测试
*/
@Test
public void testFindByName(){
List<Users> list = this.usersRepositoryByName.findByName("张三
");
for (Users users : list) {
System.out.println(users);
}
}
/**
* Repository--方法名称命名测试
*/
@Test
public void testFindByNameAndAge() {
List<Users> list = this.usersRepositoryByName.findByNameAndAge("张三", 20);
for (Users users : list) {
System.out.println(users);
}
}
/**
* Repository--方法名称命名测试*/
@Test
public void testFindByNameLike(){
List<Users> list = this.usersRepositoryByName.findByNameLike(" 张%");
for (Users users : list) {
System.out.println(users);
}
}
2 基于@Query 注解查询与更新
2.1编写接口
public interface UsersRepositoryQueryAnnotation extends Repository<Users, Integer> {
@Query("from Users where name = ?")
List<Users> queryByNameUseHQL(String name);
@Query(value="select * from t_users where name = ?",nativeQuery=true)
List<Users> queryByNameUseSQL(String name);
@Query("update Users set name = ? where id = ?")
@Modifying // 需要执行一个更新操作
void updateUsersNameById(String name, Integer id);
}
2.2测试代码
/**
* Repository--@Query 测试
*/
@Test
public void testQueryByNameUseHQL() {
List<Users> list = this.usersRepositoryQueryAnnotation.queryByNameUseHQL("张三");
for (Users users : list) {
System.out.println(users);
}
}
/**
* Repository--@Query 测试
*/
@Test
public void testQueryByNameUseSQL() {
List<Users> list = this.usersRepositoryQueryAnnotation.queryByNameUseSQL("张三");
for (Users users : list) {
System.out.println(users);
}
}
/**
* Repository--@Query 测试
*/
@Test
@Transactional //@Transactional 与@Test 一起使用时 事务是自动回滚的。
@Rollback(false) //取消自动回滚
public void testUpdateUsersNameById() {
this.usersRepositoryQueryAnnotation.updateUsersNameById("张三三", 1);
}
五、 CrudRepository 接口
1(1) CrudRepository接口的作用是什么?
主要是完成一些增删改查的操作。
(2) CrudRepository接口的继承结构是什么样的?
CredRepository 接口继承 Repository 接口
(3) CrudRepository接口中有哪些常见方法?
2 编写接口
/**
* CrudRepository 接口
*
*
*/
public interface UsersRepositoryCrudRepository extends CrudRepository<Users, Integer> { }
3 测试代码
/**
* CrudRepository 测试
*/
@Test
public void testCrudRepositorySave() {
Users user = new Users();
user.setAddress("天津");
user.setAge(32);
user.setName("张三丰");
this.usersRepositoryCrudRepository.save(user);
}
/**
* CrudRepository 测试
*/
@Test
public void testCrudRepositoryUpdate() {
Users user = new Users();
user.setId(4);
user.setAddress("南京");
user.setAge(40);
user.setName("张三丰");
this.usersRepositoryCrudRepository.save(user);
}
/**
* CrudRepository 测试
*/
@Test
public void testCrudRepositoryFindOne() {
Users users = this.usersRepositoryCrudRepository.findOne(4);
System.out.println(users);
}
/**
* CrudRepository 测试
*/
@Test
public void testCrudRepositoryFindAll() {
List<Users> list = (List<Users>) this.usersRepositoryCrudRepository.findAll();
for (Users users : list) {
System.out.println(users);
}
}
/**
* CrudRepository 测试
*/
@Test
public void testCrudRepositoryDeleteById() {
this.usersRepositoryCrudRepository.delete(4);
}
六、 PagingAndSortingRepository 接口
1
(1) PagingAndSortingRepository接口的作用是什么?
分页,排序
(2) PagingAndSortingRepository的继承结构是什么样的?
接口继承了 CrudRepository 接口
2 编写接口
/**
*
*PagingAndSortingRepository 接口
*
*/
public interface UsersRepositoryPagingAndSorting extends PagingAndSortingRepository<Users,Integer> { }
3 测试代码
/**
* PagingAndSortingRepository 排序测试
*/
@Test
public void testPagingAndSortingRepositorySort() {
// Order 定义排序规则
Order order = new Order(Direction.DESC, "id");
// Sort 对象封装了排序规则
Sort sort = new Sort(order);
List<Users> list = (List<Users>) this.usersRepositoryPagingAndSorting.findAll(sort);
for (Users users : list) {
System.out.println(users);
}
}
/**
* PagingAndSortingRepository 分页测试
*/
@Test
public void testPagingAndSortingRepositoryPaging() {
// Pageable:封装了分页的参数,当前页,每页显示的条数。注意:他的当前 页是从 0 开始。
// PageRequest(page,size) page:当前页。size:每页显示的条数
Pageable pageable = new PageRequest(1, 2);
Page<Users> page = this.usersRepositoryPagingAndSorting.findAll(pageable);
System.out.println("总条数:" + page.getTotalElements());
System.out.println("总页数" + page.getTotalPages());
List<Users> list = page.getContent();
for (Users users : list) {
System.out.println(users);
}
}
/**
* PagingAndSortingRepository 排序+分页
*/
@Test
public void testPagingAndSortingRepositorySortAndPaging() {
Sort sort = new Sort(new Order(Direction.DESC, "id"));
Pageable pageable = new PageRequest(1, 2, sort);
Page<Users> page = this.usersRepositoryPagingAndSorting.findAll(pageable);
System.out.println("总条数:" + page.getTotalElements());
System.out.println("总页数" + page.getTotalPages());
List<Users> list = page.getContent();
for (Users users : list) {
System.out.println(users);
}
}
七、 JpaRepository 接口
1
(1) JpaRepository接口的作用是什么?
对继承的父接口中的方法的返回值进行适配。
(2) JpaRepository继承结构是什么样的?
继承了 PagingAndSortingRepository 接口。
2 编写接口
/**
* 参数一 T :当前需要映射的实体
* 参数二 ID :当前映射的实体中的 OID 的类型
*
*/
public interface UsersRepository extends JpaRepository<Users,Integer>{ }
3 测试代码
/**
* JapRepository 排序测试
*/
@Test
public void testJpaRepositorySort() {
// Order 定义排序规则
Order order = new Order(Direction.DESC, "id");
// Sort 对象封装了排序规则
Sort sort = new Sort(order);
List<Users> list = this.usersRepository.findAll(sort);
for (Users users : list) {
System.out.println(users);
}
}
八、 JPASpecificationExecutor 接口
1
(1) JPASpecificationExecutor接口的作用是什么?
提供了多条件查询的支持,并且可以在查询中添加分页与排序。
(2) JPASpecificationExecutor接口的继承结构是什么样的?
JPASpecificationExecutor 是单独存在,完全独立。
(3) Specification对象的作用是什么?
用于封装查询条件
2 编写接口
/**
*
*JpaSpecificationExecutor
*
*/
public interface UsersRepositorySpecification extends JpaRepository<Users, Integer>, JpaSpecificationExecutor<Users> {
}
3 测试代码
/**
* JpaSpecificationExecutor 单条件测试
*/
@Testpublic
void testJpaSpecificationExecutor1() {
/**
* Specification<Users>:用于封装查询条件
*/
Specification<Users> spec = new Specification<Users>() {
// Predicate:封装了 单个的查询条件
/**
* Root<Users> root:查询对象的属性的封装。 CriteriaQuery<?> query:封装了我们要执行的查询中的各个部分
* 的信息,select from order by CriteriaBuilder cb:查询条件的构造器。定义不同的查询条件
*/
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// where name = '张三三'
/**
* 参数一:查询的条件属性 参数二:条件的值
*/
Predicate pre = cb.equal(root.get("name"), "张三三");
return pre;
}
};
List<Users> list = this.usersRepositorySpecification.findAll(spec);
for (Users users : list) {
System.out.println(users);
}
}
/**
* JpaSpecificationExecutor 多条件测试
*/
@Test
public void testJpaSpecificationExecutor2() {
/**
* Specification<Users>:用于封装查询条件
*/
Specification<Users> spec = new Specification<Users>() {// Predicate:封装了 单个的查询条件
/**
* Root<Users> root:查询对象的属性的封装。 CriteriaQuery<?> query:封装了我们要执行的查询中的各个部分
* 的信息,select from order by CriteriaBuilder cb:查询条件的构造器。定义不同的查询条件
*/
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// where name = '张三三' and age = 20
List<Predicate> list = new ArrayList<>();
list.add(cb.equal(root.get("name"), "张三三"));
list.add(cb.equal(root.get("age"), 20));
Predicate[] arr = new Predicate[list.size()];
return cb.and(list.toArray(arr));
}
};
List<Users> list = this.usersRepositorySpecification.findAll(spec);
for (Users users : list) {
System.out.println(users);
}
}
4 多条件查询的第二种写法
/**
* JpaSpecificationExecutor 多条件测试第二种写法
*/
@Test
public void testJpaSpecificationExecutor3() {
/**
* Specification<Users>:用于封装查询条件
*/
Specification<Users> spec = new Specification<Users>() {
// Predicate:封装了 单个的查询条件
/**
* Root<Users> root:查询对象的属性的封装。 CriteriaQuery<?>
* query:封装了我们要执行的查询中的各个部分的信息,select from order by CriteriaBuilder
* cb:查询条件的构造器。定义不同的查询条件
*/
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// where name = '张三三' and age = 20
/*
* List<Predicate> list = new ArrayList<>();
* list.add(cb.equal(root.get("name"),"张三三"));
* list.add(cb.equal(root.get("age"),20)); Predicate[] arr = new
* Predicate[list.size()];
*/
// (name = '张三' and age = 20) or id = 2
return cb.or(cb.and(cb.equal(root.get("name"), "张三三 "), cb.equal(root.get("age"), 20)),
cb.equal(root.get("id"), 2));
}
};
Sort sort = new Sort(new Order(Direction.DESC, "id"));
List<Users> list = this.usersRepositorySpecification.findAll(spec, sort);
for (Users users : list) {
System.out.println(users);
}
}
九、 关联映射操作
1 一对多的关联关系
需求:角色与用户的一对多的关联关系。
角色:一方
用户:多方
1.1Users
@Entity
@Table(name="t_users")
public class Users {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private Integer id;
@Column(name="name")
private String name;
@Column(name="age")
private Integer age;
@Column(name="address")
private String address;
@ManyToOne
//@JoinColumn:维护外键 @JoinColumn(name="roles_id")
private Roles roles;
...
}
1.2Roles
@Entity
@Table(name = "t_roles")
public class Roles {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "roleid")
private Integer roleid;
@Column(name = "rolename")
private String rolename;
@OneToMany(mappedBy = "roles")
private Set<Users> users = new HashSet<>();
...
}
1.3测试一对多的关联关系
/*** 一对多关联关系测试 ***/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = App.class)
public class OneToManyTest {
@Autowired
private UsersRepository usersRepository;
/*** 一对多关联关系的添加 */
@Test
public void testSave() {
// 创建一个用户
Users users = new Users();
users.setAddress("天津");
users.setAge(32);
users.setName("小刚");
// 创建一个角色
Roles roles = new Roles();
roles.setRolename("管理员");
// 关联
roles.getUsers().add(users);
users.setRoles(roles);
// 保存
this.usersRepository.save(users);
}
/*** 一对多关联关系的查询 */
@Test
public void testFind(){
Users findOne = this.usersRepository.findOne(4)System.out.println(findOne);
Roles roles = findOne.getRoles();
System.out.println(roles.getRolename());
}
}
2 多对多的关联关系
需求:角色与菜单多对多关联关系
角色:多方
菜单:多方
2.1Roles
@Entity
@Table(name = "t_roles")
public class Roles {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "roleid")
private Integer roleid;
@Column(name = "rolename")
private String rolename;
@OneToMany(mappedBy = "roles")
private Set<Users> users = new HashSet<>();
@ManyToMany(cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
//@JoinTable:映射中间表
//joinColumns:当前表中的主键所关联的中间表中的外键字段
@JoinTable(name="t_roles_menus",joinColumns=@JoinColumn(name="ro
le_id"),inverseJoinColumns=@JoinColumn(name="menu_id"))
private Set<Menus> menus = new HashSet<>();
...
}
2.2Menus
@Entity
@Table(name="t_menus")
public class a {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="menusid")
private Integer menusid;
@Column(name="menusname")
private String menusname;
@Column(name="menusurl")
private String menusurl;
@Column(name="fatherid")
private Integer fatherid;
@ManyToMany(mappedBy="menus")
private Set<Roles> roles = new HashSet<>();
...
}
2.3测试多对多的关联关系
/**
* 多对多的关联关系的测试
*
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = App.class)
public class ManyToManyTest {
@Autowired
private RolesRepository rolesRepository;
/**
* 添加测试
*/
@Test
public void testSave() {
// 创建角色对象
Roles r = new Roles();
r.setRolename("项目经理");
// 创建菜单对象
Menus menus = new Menus();
menus.setMenusname("xxxx 管理系统");
menus.setFatherid(0);
Menus menus2 = new Menus();
menus2.setFatherid(1);
menus2.setMenusname("项目管理");
// 关联
r.getMenus().add(menus);
r.getMenus().add(menus2);
menus.getRoles().add(r);
menus2.getRoles().add(r);
// 保存
this.rolesRepository.save(r);
}
/**
* 查询操作
*/
@Testpublic
void testFind() {
Roles roles = this.rolesRepository.findOne(2);
System.out.println(roles.getRolename());
Set<Menus> menus = roles.getMenus();
for (Menus menus2 : menus) {
System.out.println(menus2);
}
}
}