1.简介
2.整合Spring Data JPA
3.JPA提供的核心接口
4.关联映射
1.简介
Spring Data:
Spring提供了一个操作数据的框架,而Spring Data JPA只是Spring Data框架下一个基于JPA标准操作数据的模块
Spring Data JPA:
基于JPA标准对数据进行操作。简化操作持久层代码,只需要编写接口。
2.整合Spring Data JPA
- 搭建整合环境
修改pom文件,添加坐标
<!-- springBoot整合jpa的启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 测试工具的启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
- 配置application.properties
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm
spring.datasource.username=root
spring.datasource.password=951105
#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.jpa.hibernate.ddl-auto=update
# 显示sql语句
spring.jpa.show-sql=true
- 添加实体类
3.1 添加注解@Entity,表示当前类是实体类。
3.2 添加注解@Table(name = "t_users"),指定数据库中的表名
3.3 添加注解@Id, 表明当前属性为主键
设置主键生成策略 @GeneratedValue(strategy = GenerationType.IDENTITY)
3.4 @Column(name = "id") 表示属性与数据库表中的哪一列的映射关系
@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;
- 编写Dao接口
public interface UsersRepository extends JpaRepository<Users, Integer> {
}
- 编写启动类
- 编写测试代码
通过JPA生成表,并添加数据
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = App.class)
public class UsersRepositoryTest {
@Autowired
private UsersRepository usersRepository;
@Test
public void testSave(){
Users users = new Users();
users.setName("张三");
users.setAge(18);
users.setAddress("china");
this.usersRepository.save(users);
}
}
3.Spring Data JPA提供的核心接口
- Repository接口
1.1 方法名称命名查询方式
1.2 基于@Query注解查询与更新 - CrudRepository接口
- PagingAndSortingRepository接口
- JpaRepository接口
- JPASpecificationExecutor接口
1. Repository接口
1.1 方法名称命名查询方式
方法的名称必须要遵循驼峰式命名规则。findBy(关键字)+属性名称(首字母要大写)
+查询条件(首字母大写)
编写Dao接口
public interface UsersRepositoryByName extends Repository<Users, Integer> {
//方法的名称必须要遵循驼峰式命名规则。findBy(关键字)+属性名称(首字母要大写)+查询条件(首字母大写)
List<Users> findByName(String name);
List<Users> findByNameAndAge(String name,Integer age);
List<Users> findByNameLike(String name);
}
编写测试类
@Test
public void testFindByName(){
List<Users> list = this.usersRepositoryByName.findByName("张三");
for(Users users :list){
System.out.println(users);
}
}
1.2 基于@Query注解查询与更新
- 查询直接加@Query注解
- 需要更新数据库中信息还需要添加@Modifying注解
@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. CrudRepository接口
主要完成一些增删改查的操作。继承了Repository接口,能使用其所有操作
-
save
save方法,已经加了事务的注解。由源码可以看出,save方法即是插入,也是更新。(当数据库中有该对象时,就是更新,没有时,就是插入)
save
3. PagingAndSortingRepository接口
实现排序和分页的功能,只能对全部数据进行分页与排序。
- 分页
//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);
}
- 排序
2.1 定义排序规则order
Order(Direction.DESC,"id");
第一个参数为: 设置降序,第二个参数为:选择那个字段进行排序操作
2.2 使用sort对象封装排序规则
//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);
}
- 排序加分页
将封装了排序规则的sort对象放在PageRequest方法中
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);
}
4. JpaRepository接口
该接口继承了PagingAndSortingRepository接口。对父接口中的方法的返回值进行了适配。
List<Users> list = (List<Users>)this.usersRepositoryPagingAndSorting.findAll(sort);
这里的findAll方法返回的是一个可迭代类型,需要强转成List。
List<Users> list = this.usersJpaRepository.findAll();
JpaRepository
在JpaRepository中,会对方法的返回值进行适配,直接返回List类型的,不需要强转。
5. JPASpecificationExecutor接口
该接口主要是提供了多条件查询的支持,并且可以在查询中添加分页与排序
- 单条件
/**
* 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);
}
Specification<Users>:用于封装查询条件
- 多条件
2.1 多条件写法一
2.2 多条件写法二
多条件写法一:
/**
* 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);
}
多条件写法二:
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);
}
- 多条件加分页
在findAll方法中,加上sort参数,spec参数照旧
Specification<Users> spec = new Specification<Users>() {
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
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);
}
4.关联映射
1. 一对多关联映射
多的一方:Users
- 添加外键
- 级联操作(新建用户时,可能会新建一个角色,需要添加级联操作,通过添加新角色信息到数据库中)
单的一方:Roles
- @OneToMany(mappedBy = "roles") 根据roles相同的属性,来添加users到Set集合中
// 添加级联操作。 保存新用户,新用户中可能有新角色,级联保存新角色信息
@ManyToOne(cascade = CascadeType.PERSIST)
// 维护外键
@JoinColumn(name = "roles_id")
private Roles roles;
@OneToMany(mappedBy = "roles")
private Set<Users> users = new HashSet<>();
保存操作与查询操作
@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.findById(5).orElse(null);
System.out.println(findOne);
Roles roles = findOne.getRoles();
System.out.println(roles.getRolename());
}
2. 多对多关联映射
@ManyToMany(cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
//@JoinTable:映射中间表
//joinColumns:当前表中的主键所关联的中间表中的外键字段
@JoinTable(name="t_roles_menus",joinColumns=@JoinColumn(name="role_id"),inverseJoinColumns=@JoinColumn(name="menu_id"))
private Set<Menus> menus = new HashSet<>();
@ManyToMany(mappedBy="menus")
private Set<Roles> roles = new HashSet<>();
网友评论