一、Spring Data JPA介紹
spring data:其實就是spring 提供的一個操作數據的框架。而spring data JPA 只是spring data 框架下的一個基於JPA標準操作數據的模塊。
spring data jpa :基於JPA的標準對數據進行操作。簡化操作持久層的代碼,只需要編寫接口就可以。
二、spring boot 整合spring data jpa
1、搭建整合環境
2、修改pom.xml文件添加所需依賴
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lxp</groupId>
<artifactId>framework</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<properties>
<java.version>1.7</java.version>
</properties>
<!-- Add typical dependencies for a web application -->
<dependencies>
<!-- Web 啓動器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Junit測試 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</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>
<!-- spring data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
</project>
3、在項目中添加application.properties文件,添加數據庫配置以及JPA配置
# 數據庫配置
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/framework
spring.datasource.username = root
spring.datasource.password = root
# SpringData JPA 配置
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql = true
4、添加實體類
@Entity
@Table(name = "sys_users")
public class SysUsers {
@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 = "addr")
private String addr;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
5、添加接口
public interface SysUsersRepository extends JpaRepository<SysUsers, Integer> {
/**
* JpaRepository<T,ID>
*
* T:當前需要映射的實體。 ID:當前映射實體中ID的類型
*/
}
6、創建啓動類
@SpringBootApplication
public class AppStart {
public static void main(String[] args) {
SpringApplication.run(AppStart.class, args);
}
}
7、編寫測試代碼
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUsersRepository sysUsersRepository;
@Test
public void save() {
SysUsers users = new SysUsers();
users.setAddr("USA");
users.setAge(12);
users.setName("lucy");
this.sysUsersRepository.save(users);
}
}
啓動測試方法,如果數據庫有插入這個人,那麼這個接口就是可行的。
三、Spring Data JPA提供的核心接口
Repository 接口
CrudRepository 接口
pagingAndSortingRepository 接口
JpaRepository 接口
JpaSpecificationExecutor 接口
四、Repository接口的使用
該接口給我們提供了兩種查詢方法:方法名稱命名查詢方式,基於@Query註解的查詢與更新。
1、方法名稱命名查詢方式
編寫接口
public interface SysUsersRepository extends Repository<SysUsers, Integer> {
/**
* 方法名稱名稱查詢方式
*
* 名稱規則:方法的名稱必須遵循駝峯式名稱規則 : findBy(關鍵字) + 屬性名稱(首字母大寫) + 查詢條件(首字母大寫)
*/
// 單條件
List<SysUsers> findByName(String name);
// 多條件(and)
List<SysUsers> findByNameAndAge(String name, int age);
// 多條件(or)
List<SysUsers> findByNameOrAge(String name, int age);
// 單條件(like)
List<SysUsers> findByNameLike(String name);
}
編寫測試代碼
package com.lxp.test.repository;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.lxp.AppStart;
import com.lxp.dao.SysUsersRepository;
import com.lxp.pojo.SysUsers;
/**
* @author lxp
* @date 2018年7月12日 上午9:32:31
* @parameter
* @return
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUsersRepository sysUsersRepository;
/**
* 單條件查詢測試
*/
@Test
public void TestfindByName() {
List<SysUsers> ls = this.sysUsersRepository.findByName("Lucy");
for (SysUsers sysUsers : ls) {
System.out.println(sysUsers);
}
List<SysUsers> ls2 = this.sysUsersRepository.findByNameLike("%Lucy%");
for (SysUsers sysUsers : ls2) {
System.out.println(sysUsers);
}
}
/**
* 多條件查詢測試
*/
@Test
public void TestfindByNameAndAge() {
List<SysUsers> ls = this.sysUsersRepository.findByNameAndAge("Lucy", 12);
for (SysUsers sysUsers : ls) {
System.out.println(sysUsers);
}
List<SysUsers> ls2 = this.sysUsersRepository.findByNameOrAge("Lucy", 12);
for (SysUsers sysUsers : ls2) {
System.out.println(sysUsers);
}
}
}
2、基於@Query註解的查詢與更新
編寫接口
public interface SysUsersRepositoryQueryAnnotation extends Repository<SysUsers, Integer> {
// 注意點:這種寫法語句中 SysUsers 必須是和實體類名稱一樣 不能是數據裏的表名稱(sys_users)
// 底層會對HQL語句就行轉換,這種方法nativeQuery默認爲false
@Query("from SysUsers where name = ?")
List<SysUsers> QueryByNameHQL(String name);
// 注意點:nativeQuery= true 說明這的語句就是正常的SQL語句,底層不會對改語句進行轉換
@Query(value = "select * from sys_users where name = ?", nativeQuery = true)
List<SysUsers> QueryByNameSQL(String name);
@Query("update SysUsers set name = ? where id =?")
@Modifying // 需要加上@Modifying Annotation
void UpdateSysUsersNameById(String name, Integer id);
}
編寫測試類
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUsersRepositoryQueryAnnotation sysUsersRepositoryQueryAnnotation;
@Test
public void TestQueryByNameHQL() {
List<SysUsers> ls = this.sysUsersRepositoryQueryAnnotation.QueryByNameHQL("Lucy");
for (SysUsers sysUsers : ls) {
System.out.println(sysUsers);
}
List<SysUsers> ls2 = this.sysUsersRepositoryQueryAnnotation.QueryByNameSQL("Lucy");
for (SysUsers sysUsers : ls2) {
System.out.println(sysUsers);
}
}
@Test
@Transactional // 注意: @Transactional和@Test一起用的時候事務是自動回滾的
// 所以需要加上@Rollback(false) 標識不回滾
@Rollback(false)
public void TestUpdateSysUsersNameById() {
this.sysUsersRepositoryQueryAnnotation.UpdateSysUsersNameById("LucyLily", 2);
}
}
五、CrudRepository接口的使用
CrudRepository接口,主要是完成一些增刪改查的操作。注意:CrudRepository接口集成了Repository接口。
編寫接口
public interface SysUsersCrudRepository extends CrudRepository<SysUsers, Integer> {
/**
* 先不需要寫接口
*/
}
編寫測試類
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUsersCrudRepository sysUsersCrudRepository;
@Test
public void TestSysUsersCrudRepositorySave() {
SysUsers users = new SysUsers();
users.setAddr("北京");
users.setAge(25);
users.setName("李四");
this.sysUsersCrudRepository.save(users);
}
@Test
public void TestSysUsersCrudRepositoryUpdate() {
SysUsers users = new SysUsers();
users.setId(5);
users.setAddr("北京海淀區");
users.setAge(25);
users.setName("李四");
this.sysUsersCrudRepository.save(users);
}
@Test
public void TestSysUsersCrudRepositoryFindOne() {
SysUsers users = this.sysUsersCrudRepository.findOne(5);
System.out.println(users);
List<SysUsers> ls = (List<SysUsers>) this.sysUsersCrudRepository.findAll();
for (SysUsers sysUsers : ls) {
System.out.println(sysUsers);
}
}
@Test
public void TestSysUsersCrudRepositoryDel() {
this.sysUsersCrudRepository.delete(4);
}
}
注意:在試用CrudRepository接口的時候無需自己添加@Transactional回滾,因爲CrudRepository爲需要添加事務的方法已經添加了事務。
六、PagingAndSortRepository接口使用
pagingAndSortRepository接口,提供了分頁與排序的操作,注意:該接口集成了CrudRepository接口。
編寫接口
public interface SysUsersPagingAndSortRepository extends PagingAndSortingRepository<SysUsers, Integer> {
}
編寫測試類
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUsersPagingAndSortRepository sysUsersPagingAndSortRepository;
@Test
public void TestSysUsersPagingAndSortRepositorySort() {
Order order = new Order(Direction.DESC, "id"); // 定義排序規則
Sort sort = new Sort(order); // 封裝了排序的規則
Iterable<SysUsers> ls = this.sysUsersPagingAndSortRepository.findAll(sort);
for (SysUsers sysUsers : ls) {
System.out.println(sysUsers);
}
}
@Test
public void TestSysUsersPagingAndSortRepositoryPaging() {
// Pageable:封裝了分頁的參數:當前頁,每頁顯示的條數,注意:他的當前也開始數
// Pageable pageable = new PageRequest(page, size, sort);
// page:當前頁 ,size :每頁顯示的條數,sort: 排序
Order order = new Order(Direction.DESC, "id"); // 定義排序規則
Sort sort = new Sort(order); // 封裝了排序的規則
Pageable pageable = new PageRequest(0, 2, sort);
Page<SysUsers> page = this.sysUsersPagingAndSortRepository.findAll(pageable);
System.out.println("總條數:" + page.getTotalElements());
System.out.println("總頁數:" + page.getTotalPages());
List<SysUsers> ls = page.getContent();
for (SysUsers sysUsers : ls) {
System.out.println(sysUsers);
}
}
}
七、JpaRepository 接口使用
JpaRepository接口特點:該接口集成PagingAndSortingRepository接口,該接口對繼承的父接口中方法的返回值進行適配。
編寫接口
public interface SysUsersJpaRepository extends JpaRepository<SysUsers, Integer> {
/**
* Repository<T,ID>
*
* T:當前需要映射的實體。 ID:當前映射實體中ID的類型
*/
}
編寫測試類
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUsersJpaRepository sysUsersJpaRepository;
@Test
public void TestSysUsersJpaRepository() {
Order order = new Order(Direction.DESC, "id");
Sort sort = new Sort(order);
List<SysUsers> ls = this.sysUsersJpaRepository.findAll(sort);
for (SysUsers sysUsers : ls) {
System.out.println(sysUsers);
}
}
}
八、JPASpecificationExecutor接口的使用
JPASpecificationExecutor接口:該接口主要是提供了多條件查詢的支持,並且可以在查詢中添加分頁和排序。
注意:JPASpecificationExecutor接口單獨存在,完全獨立。使用時需配合其他接口使用。
編寫接口
public interface SysUserJPASpecificationExecutor extends JpaSpecificationExecutor<SysUsers>, JpaRepository<SysUsers, Integer> {
}
編寫測試類
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUserJPASpecificationExecutor sysUserJPASpecificationExecutor;
/**
* 單條件查詢
*/
@Test
public void TestSysUsersJpaRepository() {
/**
* Specification<SysUsers>:用戶封裝查詢條件。
*/
Specification<SysUsers> spec = new Specification<SysUsers>() {
@Override
public Predicate toPredicate(Root<SysUsers> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
/**
* Predicate: 封裝了單個查詢條件; root: 查詢對象屬性的封裝。
* query:封裝了要查詢中的各個部分的信息(eg:select from order by) cb:查詢條件的構造器
*/
// where name = 'Lucy'
Predicate pre = cb.equal(root.get("name"), "LucyLily");
return pre;
}
};
List<SysUsers> ls = this.sysUserJPASpecificationExecutor.findAll(spec);
for (SysUsers user : ls) {
System.out.println(user);
}
}
/**
* 多條件查詢
*/
@Test
public void TestSysUsersJpaRepository2() {
Specification<SysUsers> spec = new Specification<SysUsers>() {
@Override
public Predicate toPredicate(Root<SysUsers> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// where name = ? and age = ?
List<Predicate> list = new ArrayList<>();
list.add(cb.equal(root.get("name"), "LucyLily"));
list.add(cb.equal(root.get("age"), 12));
Predicate[] arr = new Predicate[list.size()];
return cb.and(list.toArray(arr)); // cb.or(list.toArray(arr));
/**
* 第二種多條件方式
*/
// return cb.and(cb.equal(root.get("name"), "LucyLily"),cb.equal(root.get("age"), 12));
}
};
Sort sort = new Sort(new Order(Direction.DESC, "id"));
List<SysUsers> ls = this.sysUserJPASpecificationExecutor.findAll(spec, sort);
for (SysUsers user : ls) {
System.out.println(user);
}
}
}
九、關聯映射操作
1、一對多的關聯關係
需求:角色與用戶一對多的關聯關係
角色:一方
用戶:多方
創建實體類 SysUsers.java與SysRoles.java
@Entity
@Table(name = "sys_users")
public class SysUsers {
@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 = "addr")
private String addr;
@ManyToOne
@JoinColumn(name = "rolesId")
private SysRoles sysRoles;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public SysRoles getSysRoles() {
return sysRoles;
}
public void setSysRoles(SysRoles sysRoles) {
this.sysRoles = sysRoles;
}
}
@Entity
@Table(name = "sys_roles")
public class SysRoles {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "rolesId")
private Integer rolesId;
@Column(name = "rolesName")
private String rolesName;
@OneToMany(mappedBy = "sysRoles")
private Set<SysUsers> sysUsers = new HashSet<>();
public Integer getRolesId() {
return rolesId;
}
public void setRolesId(Integer rolesId) {
this.rolesId = rolesId;
}
public String getRolesName() {
return rolesName;
}
public void setRolesName(String rolesName) {
this.rolesName = rolesName;
}
public Set<SysUsers> getSysUsers() {
return sysUsers;
}
public void setSysUsers(Set<SysUsers> sysUsers) {
this.sysUsers = sysUsers;
}
}
測試一對多的關聯關係(編寫測試類)
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUsersJpaRepository sysUsersJpaRepository;
@Test
public void TestOneToManyAdd() {
// 創建一個用戶
SysUsers user = new SysUsers();
user.setAddr("AA....");
user.setAge(30);
user.setName("AAAAAA");
// 創建一個角色
SysRoles role = new SysRoles();
role.setRolesName("超級管理員");
// 用戶角色關聯
role.getSysUsers().add(user);
user.setSysRoles(role);
// 保存
sysUsersJpaRepository.save(user);
}
@Test
public void TestOneToManyQuery() {
SysUsers user = sysUsersJpaRepository.findOne(8);
System.out.println(user);
System.out.println(user.getSysRoles().getRolesName());
}
}
2、多對多的關聯關係
需求:角色與菜單多對多的關聯關係
編寫實體類SysRoles.java與Menus.java
@Entity
@Table(name = "sys_roles")
public class SysRoles {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "rolesId")
private Integer rolesId;
@Column(name = "rolesName")
private String rolesName;
@OneToMany(mappedBy = "sysRoles")
private Set<SysUsers> sysUsers = new HashSet<>();
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
// @JoinTable映射中間表
// JoinColumn 當前表的主鍵所關聯中間表中的外鍵字段
// inverseJoinColumns
@JoinTable(name = "sys_roles_menus", joinColumns = @JoinColumn(name = "rolesId"), inverseJoinColumns = @JoinColumn(name = "menusId"))
private Set<Menus> roles = new HashSet();
public Integer getRolesId() {
return rolesId;
}
public void setRolesId(Integer rolesId) {
this.rolesId = rolesId;
}
public String getRolesName() {
return rolesName;
}
public void setRolesName(String rolesName) {
this.rolesName = rolesName;
}
public Set<SysUsers> getSysUsers() {
return sysUsers;
}
public void setSysUsers(Set<SysUsers> sysUsers) {
this.sysUsers = sysUsers;
}
@Override
public String toString() {
return "SysRoles [rolesId=" + rolesId + ", rolesName=" + rolesName + "]";
}
}
編寫測試類
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AppStart.class)
public class RepositoryTest {
@Autowired
private SysUsersJpaRepository sysUsersJpaRepository;
@Test
public void TestManyToManyAdd() {
// 創建一個角色
SysRoles sysRoles = new SysRoles();
sysRoles.setRolesName("項目經理");
// 創建菜單
Menus menu = new Menus();
menu.setMenusName("系統管理");
menu.setPid(0);
Menus menu2 = new Menus();
menu2.setMenusName("基礎管理");
menu2.setPid(1);
// 關聯
sysRoles.getMenus().add(menu);
sysRoles.getMenus().add(menu2);
menu.getRoles().add(sysRoles);
menu2.getRoles().add(sysRoles);
// 保存
this.sysUsersJpaRepository.save(sysRoles);
}
@Test
public void TestManyToManyQuery() {
SysRoles roles = this.sysUsersJpaRepository.findOne(2);
System.out.println(roles);
Set<Menus> set = roles.getMenus();
for (Menus m : set) {
System.out.println(m);
}
}
}