梳理一下 SpringBoot 中持久层常用技术的概念和使用方法。
1. JDBC
JDBC(Java Data Base Connectivity)即Java数据库连接,官方解释它是Java编程语言和广泛的数据库之间独立于数据库的连接标准的Java API,根本上说JDBC是一种规范,它提供的接口,一套完整的,允许便捷式访问底层数据库。简单说它就是JAVA与数据库的连接的桥梁或者插件,用JAVA代码就能操作数据库的增删改查、存储过程、事务等。
常用接口:
- JAVA API:提供对JDBC的管理链接;
- JAVA Driver API:支持JDBC管理到驱动器连接。
- DriverManager:这个类管理数据库驱动程序的列表,查看加载的驱动是否符合JAVA Driver API的规范。
- Connection:与数据库中的所有的通信是通过唯一的连接对象。
- Statement:把创建的SQL对象,转而存储到数据库当中。
- ResultSet:它是一个迭代器,用于检索查询数据。
使用流程:
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 中有两种方式:
-
嵌套结果
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> -
关联的嵌套查询(在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>