Spring Boot 持久层知识梳理

2,103 阅读6分钟

梳理一下 SpringBoot 中持久层常用技术的概念和使用方法。

1. JDBC

JDBC(Java Data Base Connectivity)即Java数据库连接,官方解释它是Java编程语言和广泛的数据库之间独立于数据库的连接标准的Java API,根本上说JDBC是一种规范,它提供的接口,一套完整的,允许便捷式访问底层数据库。简单说它就是JAVA与数据库的连接的桥梁或者插件,用JAVA代码就能操作数据库的增删改查、存储过程、事务等。

img

常用接口:

  • JAVA API:提供对JDBC的管理链接;
  • JAVA Driver API:支持JDBC管理到驱动器连接。
  • DriverManager:这个类管理数据库驱动程序的列表,查看加载的驱动是否符合JAVA Driver API的规范。
  • Connection:与数据库中的所有的通信是通过唯一的连接对象。
  • Statement:把创建的SQL对象,转而存储到数据库当中。
  • ResultSet:它是一个迭代器,用于检索查询数据。

使用流程:

img

2. JPA

JPA(Java Persistence API)是 JDK 5.0 注解或 XML 描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。是一种标准接口。

3. ORM框架

ORM(Object Relational Mapping)框架采用元数据来描述对象与关系映射的细节,元数据一般采用 XML 格式,并且存放在专门的对象一映射文件中。简单理解为一种框架的格式

3.1 MyBatis

最大的特点是可以自由定制 mapper 中的 SQL 语句。

3.1.1 基础配置与使用

Maven 配置:

<dependency>	           	 
  <groupId>org.mybatis.spring.boot</groupId>	
  <artifactId>mybatis-spring-boot-starter</artifactId>	
  <version>2.0.1</version>	
</dependency>

YAML 配置:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # druid 连接池
    driver-class-name: com.mysql.jdbc.Driver # MySQL 数据库
    url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=true
    username: root
    password: 123456
        
mybatis:
  mapperLocations: classpath:mapper/*Mapper.xml # mapper xml 文件地址	
  type-aliases-package: com.example.entity # entity 文件地址	

entity 类:

@Data
public class User {
    private Integer id;
    private String userName;
    private String passWord;
    private String realName;
}

mapper xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
 
    <select id="select" resultType="com.example.entity.User">
        select * from user where id = #{id}
    </select>
 
</mapper>

mapper 接口:

@Repository
public interface UserMapper {
    User select(int id);
}

service 类:

@Service
public class UserService {
    @Autowired
    UserMapper userMapper;
    public User Sel(int id){
        return userMapper.Sel(id);
    }
}

controller 类:

@RestController
@RequestMapping("/testBoot")
public class UserController {
 
    @Autowired
    private UserService userService;
 
    @RequestMapping("getUser/{id}")
    public String GetUser(@PathVariable int id){
        return userService.Sel(id).toString();
    }
}

application 类:

@MapperScan("com.example.mapper") //扫描的mapper
@SpringBootApplication
public class DemoApplication {
 
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

3.1.2 resultMap

1)基础使用

entity 类:

@Data
public class product  {
    private Long id;
    private String name;
    private Long categoryId;
}

对应的 mapper:

<resultMap id="BaseResultMap" type="com.exemple.entity.Product">
    <id column="ID" jdbcType="BIGINT" property="id" />
    <result column="NAME" jdbcType="VARCHAR" property="name" />
    <result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
</resultMap> 
2)添加属性集合

entity 类:

@Data
public class Product  {
	private Long id;
	private String name;
	private Long categoryId;
	private List<Attribute> attributes;
}

将 collection 添加到 resultMap 中有两种方式:

  1. 嵌套结果

    mapper:

    <resultMap id="BasePlusResultMap" type="com.exemple.entity.Product">
        <id column="ID" jdbcType="BIGINT" property="id" />
        <result column="NAME" jdbcType="VARCHAR" property="name" />
        <result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
        <collection property="attributes" ofType="com.exemple.entity.Attribute" > 
            <id column="AttributeID" jdbcType="BIGINT" property="id" />
            <result column="attribute_NAME" jdbcType="VARCHAR" property="attributeName" />
        </collection>
    </resultMap>
    
    <select id="getById"  resultMap="basePlusResultMap">
        select s.ID,s.NAME,s.CATEGORY_ID,a.ID,a.ATTRIBUTE_NAME
        from t_shop_sku s,t_shop_attribute a 
        where s.ID =a.ID and s.ID = #{id,jdbcType =BIGINT};
    </select>
    
  2. 关联的嵌套查询(在collection中添加select属性)

    product mapper:

    <resultMap id="ProductResultMap" type="com.exemple.entity.TShopSku">
        <id column="ID" jdbcType="BIGINT" property="id" />
        <result column="NAME" jdbcType="VARCHAR" property="skuName" />
        <result column="CATEGORY_ID" jdbcType="BIGINT" property="categoryId" />
        <collection column="{skuId=ID}" property="attributes" ofType="com.exemple.entity.Attribute" select="getAttribute" > 
        </collection>
    </resultMap>
    
    <select id="getById"  resultMap="ProductResultMap">
        select s.ID,s.SKU_NAME,s.CATEGORY_ID
        from t_shop_sku s
        where  s.ID = #{id,jdbcType =BIGINT};
    </select>
    

    attribute mapper

    <resultMap id="AttributeResultMap" type="com.exemple.entity.Attribute">
        <id column="ID" jdbcType="BIGINT" property="id" />
        <result column="ATTRIBUTE_NAME" jdbcType="VARCHAR" property="attributeName" />
    </resultMap>
    
    <select id="getAttribute"  resultMap="AttributeResultMap">
        select a.ID,s.ATTRIBUTE_NAME
        from t_shop_attribute a
        where  a.ID = #{id,jdbcType =BIGINT};
    </select>
    

3.2 Spring Data JPA

3.2.1 基础配置与使用

maven:

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

YAML:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # druid 连接池
    driver-class-name: com.mysql.jdbc.Driver # MySQL 数据库
    url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=true
    username: root
    password: 123456
  jpa:
    database: MySQL
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      ddl-auto: update

其中 ddl-auto 的参数:

  • create:每次运行程序时,都会重新创建表,故而数据会丢失

  • create-drop:每次运行程序时会先创建表结构,然后待程序结束时清空表

  • upadte:每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新(推荐使用)

  • validate:运行程序会校验数据与数据库的字段类型是否相同,字段不同会报错

  • one: 禁用DDL处理

entity 类:

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

    @Id
    @GenericGenerator(name = "idGenerator", strategy = "uuid") // 主键生成策略为 uuid
    @GeneratedValue(generator = "idGenerator")
    private String id;

    @Column(name = "username", unique = true, nullable = false, length = 64)
    private String username;

    @Column(name = "password", nullable = false, length = 64)
    private String password;

    @Column(name = "email", length = 64)
    private String email;

}

DAO:

public interface UserRepository extends JpaRepository<User, String> {
}

Service:

public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User saveUser(User user) {
        return userRepository.save(user);
    }

    public void deleteUser(String userId) {
        userRepository.deleteById(userId);
    }

    public User updateUser(String userId, User user) {
        user.setId(userId);
        return userRepository.save(user);
    }

    public User getUserInfo(String userId) {
        return userRepository.findById(userId);
    }

    public Page<User> pageQuery(Integer pageNum, Integer pageSize) {
        return userRepository.findAll(PageRequest.of(pageNum - 1, pageSize));
    }

}

Controller:

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping()
    public User saveUser(@RequestBody User user) {
        return userService.save(user);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable("id") String userId) {
        userService.deleteById(userId);
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable("id") String userId, @RequestBody User user) {
        user.setId(userId);
        return userService.save(user);
    }

    @GetMapping("/{id}")
    public User getUserInfo(@PathVariable("id") String userId) {
        return userService.getUserInfo(userId);
    }

    @GetMapping("/list")
    public Page<User> pageQuery(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
                                @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
        return userService.pageQuery(pageNum, pageSize);
    }

}

3.3 Hibernate、JPA、Spring Data JPA 三者之间的区别与联系

Hibernate 是持久化实现技术,是一种 ORM 框架;

JPA 是持久化的标准,一个是具体实现,一个是接口协议;

Spring Data JPA 是在 Hibernate 的基础上更上层的封装实现。

4. 数据库连接池

4.1 Druid

阿里开源项目 Druid,自带监控界面。

maven:

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.1.9</version>
</dependency>

application 配置:

spring:
  datasource:
    druid:
      url: jdbc:mysql://127.0.0.1:3306/tmall_springboot?characterEncoding=UTF-8
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver

      # 连接池的配置信息
      initialSize: 5
      minIdle: 5
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      useGlobalDataSourceStat: true
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: stat,wall

      #配置监控属性: 在 druid-starter 的:com.alibaba.druid.spring.boot.autoconfigure.stat 包下进行的逻辑配置
      web-stat-filter: # WebStatFilter配置,
        enabled: true #默认为false,表示不使用WebStatFilter配置,就是属性名去短线
        url-pattern: /* #拦截该项目下的一切请求
        exclusions: /druid/*,*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico  #对这些请求放行
        session-stat-enable: true
        principal-session-name: session_name
        principal-cookie-name: cookie_name

      # 监控界面 StatViewServlet 配置
      stat-view-servlet: 
        enabled: true #默认为false,表示不使用StatViewServlet配置,就是属性名去短线
        url-pattern: /druid/*  #配置DruidStatViewServlet的访问地址。后台监控页面的访问地址
        reset-enable: false #禁用HTML页面上的“重置”功能,会把所有监控的数据全部清空,一般不使用
        login-username: admin #监控页面登录的用户名
        login-password: 123456 #监控页面登录的密码
        allow:   #IP白名单(没有配置或者为空,则允许所有访问)。允许谁访问druid后台,默认允许全部用户访问。
        deny:  #IP黑名单 (存在共同时,deny优先于allow)。不允许谁进行访问druid后台,
      #Spring监控配置,说明请参考Druid Github Wiki,配置_Druid和Spring关联监控配置
      aop-patterns: com.wtychn.tmall.service.*

5. 数据库

5.1 MySQL

maven 配置:

<dependency>	
    <groupId>mysql</groupId>	
    <artifactId>mysql-connector-java</artifactId>	
</dependency>