spring-boot-starter-data-jpa 解析

网上看到的,记录一下,原文:https://blog.csdn.net/Lee_Ho_/article/details/81014215

一:引言
    对于传统关系型数据库,Spring Boot使用JPA(Java Persistence API)资源库来实现对数据库的操作,简单来说,JPA就是为POJO(Plain Ordinary Java Object)提供持久化的标准规范,即将Java普通对象通过对象关系映射(Object Relational Mapping,ORM)持久化到数据库中。

二:使用方式
2.1:JPA配置
    为了使用JPA和MySQL,创建Spring Boot的Maven工程,在POM文件当中引入如下依赖:

<?xml version="1.0" encoding="UTF-8"?>
<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.example</groupId>
<artifactId>spring-boot-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-boot-jpa</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>jpa</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>
repackage
</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>


</project>
application.yml配置:

spring:
datasource:
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8
username: root
password: cuoai1995
jpa:
database: mysql
show-sql: true
#Hibernate ddl auto (validate|create|create-drop|update)
hibernate:
ddl-auto: update
naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect

2.2:实体建模:
    为了演示JPA的使用方式,建立适当的实体关系,并演示如何通过注解方式实现实体建模:

实体之间的关系如下图所示:

根据图上关系进行实体建模:

部门类:

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "department")
public class Department implements Serializable{

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
角色类:

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "role")
public class Role implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
用户类(getter and setter...):

import com.fasterxml.jackson.annotation.JsonBackReference;
import org.springframework.format.annotation.DateTimeFormat;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.List;

@Entity
@Table(name = "user")
public class User implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate;

@ManyToOne
@JoinColumn(name = "did")
@JsonBackReference
private Department department;

@ManyToMany(cascade = {}, fetch = FetchType.EAGER)
@JoinTable(name = "user_role", joinColumns = {@JoinColumn(name = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "roles_id")})
private List<Role> roles;
这里使用了一张新表user_role来表示用户表和角色表多对多的依赖关系。

2.3:Spring Boot配置JPA测试环境:
创建JPA的配置类:JpaConfiguration,相关说明见配置类注释:

import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Order(Ordered.HIGHEST_PRECEDENCE)//定义组件的加载顺序,这里为最高级
@Configuration//表明这是一个配置类
@EnableTransactionManagement(proxyTargetClass = true)//启用JPA的事物管理
@EnableJpaRepositories(basePackages = "com.example.springbootjpa.repository")//启动JPA资源库并设置接口资源库的位置
@EntityScan(basePackages = "com.example.springbootjpa.pojo")//实体类位置
public class JpaConfiguration {

/**
* @Description: 这里说明为什么要声明一个PersistenceExceptionTranslationPostProcessor 的Bean对象,引用Spring官方文档的一句话:
* (1)scanned by Spring component-scanning
* (2)catch platformspecific exceptions and rethrow them as one of Spring’s unified unchecked exceptions
But if you’re using Hibernate contextual sessions and not a Hibernate template,how can the exception
translation take place?
翻译过来就是:@Repository有两作用:
(1):用于被容器扫描:
(2):捕获平台特定的异常并将它们重新抛出,作为Spring的一个未检查的异常。(用于事务的管理,例如捕获异常回滚)
但是,如果您使用的是Hibernate contextual sessions上下文会话而不是Hibernate template,那么异常转换是如何发生的呢?
那么,这就是配置这个类的作用。
* @return
*/
@Bean
PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){
return new PersistenceExceptionTranslationPostProcessor();
}
}
三:创建实体对应的Jpa接口
用户接口:

import com.example.springbootjpa.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

User findByName(String name);
}
部门接口:

import com.example.springbootjpa.pojo.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface DepartmentRepository extends JpaRepository<Department, Long> {
}
角色接口:

import com.example.springbootjpa.pojo.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
}
四:测试
    在做完如上工作之后,就可以进行下一步测试了。

import com.example.springbootjpa.pojo.Department;
import com.example.springbootjpa.pojo.Role;
import com.example.springbootjpa.pojo.User;
import com.example.springbootjpa.repository.DepartmentRepository;
import com.example.springbootjpa.repository.RoleRepository;
import com.example.springbootjpa.repository.UserRepository;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Date;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MysqlTest {

private static Logger logger = LoggerFactory.getLogger(MysqlTest.class);

@Autowired
private UserRepository userRepository;

@Autowired
private DepartmentRepository departmentRepository;

@Autowired
private RoleRepository roleRepository;

@Before
public void initData(){
userRepository.deleteAll();
roleRepository.deleteAll();
departmentRepository.deleteAll();

Department department = new Department();
department.setName("开发部");
departmentRepository.save(department);
Assert.assertNotNull(department.getId());

Role role = new Role();
role.setName("admin");
roleRepository.save(role);
Assert.assertNotNull(role.getId());

User user = new User();
user.setName("user");
user.setCreateDate(new Date());
user.setDepartment(department);
List<Role> roles = roleRepository.findAll();
Assert.assertNotNull(roles);
user.setRoles(roles);
userRepository.save(user);
Assert.assertNotNull(user.getId());
}

@Test
public void findPage(){
Pageable pageable = PageRequest.of(0, 10, new Sort(Sort.Direction.ASC, "id"));
Page<User> page = userRepository.findAll(pageable);
Assert.assertNotNull(page);
for (User user : page.getContent()){
logger.info("====user==== user name:{}, department name:{}, role name:{}", user.getName(),
user.getDepartment().getName(), user.getRoles().get(0).getName());
}
}

@Test
public void testFindByName(){
String name = "user";
User user = userRepository.findByName(name);
Assert.assertNotNull(user);
logger.info(user.getName());
}
}
结果:

六:总结
一:为什么要继承JpaRepository?

以UserRepository为例,来看看整个的继承体系:

可见,Jpa资源库已经给我们提供了丰富的方法来满足普通的操作需求。

二:自定义方法的实现:

Jpa本身还提供了一些自定义声明方法的规则,例如:在接口中使用关键字findBy、readBy、getBy作为方法名的前缀,拼接实体类中的属性字段(首字母大写),并可选择拼接一些SQL关键字来组合成一个查询方法,例如,对于用户实体,关键字可以这样使用:

1.And,如:findByIdAndName(Long id, String name);

2.Or,如:findByIdOrName(Long id, String name);

3.Between,如:findByCreateDateBetween(Date start, Date end);

4.LessThan,如:findByCreateDateLessThan(Date start);

5.GreaterThan,如:findByCreateDateGreaterThan(Date start);

6.IsNull,如:findByNameIsNull();

7.IsNotNull,与上等价

8.Like,如:findByNameLike(String name);

9.NotLike:与上等价

10.OrderBy,如:findByNameOrderByIdAsc(String name);

11.Not,如:findByNameNot(String name);

12.In,如:findByNameIn(Collection<String> nameList);

13.NotIn,与上等价。

加入说上面这些还是不能够满足你的业务需求,你同样可以写一个自定义的方法,使用@Query注解+HQL语句实现你想要的效果。
---------------------
版权声明:本文为CSDN博主「笼中人.」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Lee_Ho_/article/details/81014215

猜你喜欢

转载自www.cnblogs.com/linwenbin/p/11357368.html