一:引言

    对于传统关系型数据库,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/POM/4.0.0" xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/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语句实现你想要的效果。

更多推荐

Spring Boot (一):spring-boot-starter-data-jpa 解析