Spring Boot 整合 Spring Data JPA

本文导读

  • 关于 Spring Data、JPA、Spring Data JPA 等理论只是可以参考《Spring Data JPA 理论简介
  • Sping Data JPA 底层默认由 Hibernate 实现,同样基于 ORM(对象关系型映射),但操作比 Hibernate 更简单,以前 Hibernate 还需要程序员封装 BaseDao 层,而 Spring Data JPA 连这一步都省略了,实现接口之后,就可以直接调用其中的 CRUD 方法以及排序、分页等方法,更加简洁。
  • Spring Data 提供统一的 Repository(仓库) 接口
  1. Repository<T, ID extends Serializable>:统一接口,根接口
  2. RevisionRepository<T, ID extends Serializable, N extends Number & Comparable<N>>:基于乐观锁机制
  3. CrudRepository<T, ID extends Serializable>:通用 CRUD 操作接口
  4. PagingAndSortingRepository<T, ID extends Serializable>:通用 CRUD 及分页、排序等操作操作
  5. JpaRepository:操作关系型数据库时,只要继承此接口,则相当于有了 CRUD、分页、排序等常用的所有方法,程序员可以直接调用。

  • 为了思路清晰,本文将新建项目

环境准备

新建项目

默认 pom. xml 文件

<?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>www.wmx.com</groupId>
    <artifactId>seals</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>seals</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.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>
        <!-- 引入 Spring Data JPA-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- 引入 JDBC,只要操作数据就得有  JDBC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!-- Html 的魔板引擎 Thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!-- web应用必须引入的 web 模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Mysql 驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Spring 官方的测试模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

Spring Data JPA CRUD

数据源配置

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/seals?characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver
  • 可以测试一下获取数据源是否成功
package com.lct.www;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

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


    /**
     * Spring Boot 默认已经配置好了数据源,程序员可以直接 DI 注入然后使用即可
     */
    @Resource
    DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {

        System.out.println("数据源>>>>>>" + dataSource.getClass());
        Connection connection = dataSource.getConnection();

        System.out.println("连接>>>>>>>>>" + connection);
        System.out.println("连接地址>>>>>" + connection.getMetaData().getURL());
        connection.close();
    }
}
  • 控制台输出如下:

数据源>>>>>>class com.zaxxer.hikari.HikariDataSource
连接>>>>>>>>>HikariProxyConnection@565077371 wrapping com.mysql.jdbc.JDBC4Connection@799f916e
连接地址>>>>>jdbc:mysql://localhost:3306/seals?characterEncoding=UTF-8

2018-08-25 11:21:01.428  INFO 17440 --- [       Thread-2] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@210ab13f: startup date [Sat Aug 25 11:20:56 CST 2018]; root of context hierarchy
2018-08-25 11:21:01.433  INFO 17440 --- [       Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2018-08-25 11:21:01.434  INFO 17440 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2018-08-25 11:21:01.441  INFO 17440 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

Process finished with exit code 0

domain Area

  • 提供封装数据库表 area 的 POJO ,以及配置与数据库表的映射关系
  • 因为 Spring Data JPA 也是基于 ORM 思想,而且底层又是 Hibernate 实现,所以 POJO 与数据库表的映射关系写法与以前写 Hibernate 时基本无异
  • 同理可以自己在数据库手动建表,也可以通过 Hibernate 根据映射关系自动建表,本文将演示后者。
  • 注意再提醒一次:所有需要 Spring Boot 自动扫描的注解必须放在应用启动类同目录下

package com.lct.www.domain;

import javax.persistence.*;
import java.util.Date;

/**
 * Created by Administrator on 2018/8/25 0025.
 * 区域--------实体类
 *
 * @Entity :告诉 JPA 本类是一个与数据库表进行映射的实体类,而不是普通的 Java Bean
 * @Table :指定本映射实体类与数据库哪个表进行映射,不写时默认为类名首字母小写(area)
 */

@Entity
@Table(name = "area")
public class Area {

    /**
     * @Id : 指定此字段为数据库主键
     * @GeneratedValue :指定主键生成的策略(strategy),可以选择如下:
     * TABLE,
     * SEQUENCE:由 DB2、Oracle、SAP DB 等数据库 使用自己的 序列 进行管理生成
     * IDENTITY:由数据库自己进行管理,如 Mysql 的 自动递增
     * AUTO:让ORM框架自动选择,默认值
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    /**
     * @Column :指定此属性映射数据库表的哪个字段,不写时默认为属性名
     * length :指定此字段的长度,只对字符串有效,不写时默认为255
     * unique :是否添加唯一约束,不写时默认为 false
     * 更多详细属性,可以进入 javax.persistence.Column 查看
     */
    @Column(name = "name", length = 32, unique = true)
    private String name;

    /**
     * nullable :表示此字段是否允许为null
     */
    @Column(nullable = false)
    private String clients;
    private Date createTime;

    public String getClients() {
        return clients;
    }

    public void setClients(String clients) {
        this.clients = clients;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    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;
    }

    @Override
    public String toString() {
        return "Area{" +
                "clients='" + clients + '\'' +
                ", id=" + id +
                ", name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

自定义 Dao 接口

  • 编写一个 Dao 接口(Repository)来操作实体类对应的数据表,实现了如下的接口,就拥有了其对应的方法。

  • 因为使用的是 Spring Data JPA,所以直接继承 JpaRepository 接口,之后便可以支持从 service 层或者 Controller层调用其方法。

package com.lct.www.dao;

import com.lct.www.domain.Area;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Created by Administrator on 2018/8/25 0025.
 * 自定义接口继承 JpaRepository<T, ID> 即可
 * T :泛型,传入操作的实体类即可
 * TD:传入实体类主键的类型
 * 如果是以前 Hibernate 则还需要在 Dao 层的实现类上加 @Repository 注解注入每一个 Dao实现组件
 * 而 Spring Data JPA 的数据库访问层就已经完成了,继承了JpaRepository接口,就拥有了所有的 CRUD方法、排序、分页方法
 * 继承 JpaRepository 之后,本接口就已经是 JPA 的 @Repository 了,所以不要再加,直接在 service层或者controller层注入即可
 */
public interface AreaDao extends JpaRepository<Area, Integer> {
}

JPA 配置

  • Spring Boot 提供了配置 JPA,如以前 Hibernate 时期的是否显示 sql 语句,对表策略等,如下所示只是写法略有不同,意义与以前 Hibernate 时完全一样。
  • 如果是自己在数据库中手动建表,则省略 "ddl-auto" 的对表策略配置即可
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/seals?characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver

#JPA 配置,可以参考官网:https://docs.spring.io/spring-boot/docs/2.0.4.RELEASE/reference/htmlsingle/#common-application-properties
  jpa:
    hibernate:
# 更新或者创建数据库表结构,省略时将不会自动生成数据库表
       ddl‐auto: update
# 控制台是否显示SQL
       show‐sql: true
# JPA (JpaBaseConfiguration, HibernateJpaAutoConfiguration)
spring.data.jpa.repositories.enabled=true # Whether to enable JPA repositories.
spring.jpa.database= # Target database to operate on, auto-detected by default. Can be alternatively set using the "databasePlatform" property.
spring.jpa.database-platform= # Name of the target database to operate on, auto-detected by default. Can be alternatively set using the "Database" enum.
spring.jpa.generate-ddl=false # Whether to initialize the schema on startup.
spring.jpa.hibernate.ddl-auto= # DDL mode. This is actually a shortcut for the "hibernate.hbm2ddl.auto" property. Defaults to "create-drop" when using an embedded database and no schema manager was detected. Otherwise, defaults to "none".
spring.jpa.hibernate.naming.implicit-strategy= # Fully qualified name of the implicit naming strategy.
spring.jpa.hibernate.naming.physical-strategy= # Fully qualified name of the physical naming strategy.
spring.jpa.hibernate.use-new-id-generator-mappings= # Whether to use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE.
spring.jpa.mapping-resources= # Mapping resources (equivalent to "mapping-file" entries in persistence.xml).
spring.jpa.open-in-view=true # Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request.
spring.jpa.properties.*= # Additional native properties to set on the JPA provider.
spring.jpa.show-sql=false # Whether to enable logging of SQL statements.

测试生成表

AreaController 

  • 省略 service层,直接浏览器访问控制层,然后进行 CRUD 操作,操作结果直接返回给页面,不做跳转

package com.lct.www.controllr;

import com.lct.www.dao.AreaDao;
import com.lct.www.domain.Area;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;

/**
 * Created by Administrator on 2018/8/25 0025.
 * 区域---控制层
 */
@Controller
public class AreaController {

    @Resource
    private AreaDao areaDao;

    /**
     * 根据id 查询
     *
     * @param id
     * @return
     */
    @ResponseBody
    @GetMapping("/seals/{id}")
    public Area findAreaById(@PathVariable("id") Integer id, HttpServletResponse response) {
        System.out.println("com.lct.www.controllr.AreaController.findAreaById:::" + id);
        /**
         * 推荐使用 findById 方式,而不是 getOne方法
         * isPresent 判断 Optional是否为空,即有没有值
         */
        Optional<Area> areaOptional = areaDao.findById(id);
        if (!areaOptional.isPresent()) {
            return null;
        } else {
            System.out.println("area2::" + areaOptional.get());
            /** 获取Area值*/
            return areaOptional.get();
        }
    }

    /**
     * 查询所有
     *
     * @return
     */
    @ResponseBody
    @GetMapping("/seals")
    public List<Area> findAllAreas() {
        List<Area> areaList = areaDao.findAll();
        return areaList;
    }

    /**
     * 添加 区域
     * localhost:8080/seals/save?name=党建学习区&clients=1,2&createTime=2018/08/12
     * localhost:8080/seals/save?name=一学一做区&clients=4,5,6&createTime=2018/08/15
     *
     * @param area
     * @return 添加成功后重定向到查询所有
     */
    @GetMapping("seals/save")
    public String saveArea(Area area) {
        /**
         * save 方法:当实体的主键不存在时,则添加;实体的主键存在时,则更新
         */
        areaDao.save(area);
        return "redirect:/seals";
    }

    /**
     * 更新区域
     * localhost:8080/seals/save?id=2&name=一学一做区2&clients=4,5,6&createTime=2018/08/15
     *
     * @param area
     * @return
     */
    @GetMapping("/seals/update")
    public String updateArea(Area area) {
        /**
         * save 方法:当实体的主键不存在时,则添加;实体的主键存在时,则更新
         */
        areaDao.save(area);
        return "redirect:/seals";
    }

    /**
     * 根据主键 id 删除区域
     *
     * @return
     */
    @GetMapping("/seals/del/{id}")
    public String deleteAreaById(@PathVariable("id") Integer id) {
        areaDao.deleteById(id);
        return "redirect:/seals";
    }


    /**
     * 删除表中所有数据
     *
     * @return
     */
    @GetMapping("seals/delAll")
    public String deleteAll() {
        areaDao.deleteAll();
        return "redirect:/seals";
    }

    /**
     * 另外还有分页查询、排序查询等,在此就不再一一测试
     */
}

CRUD 测试

  • 添加测试:

  • 查询测试:

  • 修改测试:

  • 删除测试:

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/82048775