11.数据访问
JDBC
对于数据访问层,无论是 SQL(关系型数据库) 还是 NOSQL(非关系型数据库),Spring Boot 底层都是采用 Spring Data 的方式进行统一处理。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
配置数据源
spring: datasource: username: root password: root # 时区报错加serverTimezone=UTC url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.jdbc.Driver
效果:
默认是用org.apache.tomcat.jdbc.pool.DataSource作为数据源;
数据源的相关配置都在DataSourceProperties里面;
jdbc自动配置原理
org.springframework.boot.autoconfigure.jdbc
包下
package org.springframework.boot.autoconfigure.jdbc; abstract class DataSourceConfiguration { @SuppressWarnings("unchecked") protected <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) { return (T) properties.initializeDataSourceBuilder().type(type).build(); } /** * Tomcat Pool DataSource configuration. */ @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true) static class Tomcat extends DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.tomcat") public org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) { org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource( properties, org.apache.tomcat.jdbc.pool.DataSource.class); DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl()); String validationQuery = databaseDriver.getValidationQuery(); if (validationQuery != null) { dataSource.setTestOnBorrow(true); dataSource.setValidationQuery(validationQuery); } return dataSource; } } // HikariDataSource、dbcp等等其他的数据源 }
参考DataSourceConfiguration
,根据配置创建数据源,默认使用Tomcat连接池;可以使用spring.datasource.type
指定自定义的数据源类型;
SpringBoot默认可以支持数据源:
org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、dbcp.BasicDataSource、dbcp2
若没有使用以上默认的数据源可以自定义数据源类型,在配置文件中指定类型:
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
自定义数据源类型源码:
abstract class DataSourceConfiguration { /** * Generic DataSource configuration. */ @ConditionalOnMissingBean(DataSource.class) @ConditionalOnProperty(name = "spring.datasource.type") static class Generic { @Bean public DataSource dataSource(DataSourceProperties properties) { //使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性 return properties.initializeDataSourceBuilder().build(); } } } // build方法中通过反射绑定相关属性 public class DataSourceBuilder { public DataSource build() { Class<? extends DataSource> type = getType(); DataSource result = BeanUtils.instantiate(type); maybeGetDriverClassName(); bind(result); return result; } }
Druid数据源
Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP、PROXOOL 等 DB 池的优点,同时加入了日志监控。
Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。
基础配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver # 时区报错加serverTimezone=UTC spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=root # 配置type改变需要的数据源 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.initialSize=5 spring.datasource.minIdle=5 spring.datasource.maxActive=20 spring.datasource.maxWait=60000 spring.datasource.timeBetweenEvictionRunsMillis=60000 spring.datasource.minEvictableIdleTimeMillis=300000 spring.datasource.validationQuery=SELECT 1 FROM DUAL spring.datasource.testWhileIdle=true spring.datasource.testOnBorrow=false spring.datasource.testOnReturn=false spring.datasource.poolPreparedStatements=true #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 #如果允许时报错 # java.lang.ClassNotFoundException: org.apache.log4j.Priority # 则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j spring.datasource.filters=stat,wall,log4j spring.datasource.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.useGlobalDataSourceStat=true spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
Spring Boot 2.0 以上默认使用 com.zaxxer.hikari.HikariDataSource 数据源,但可以 通过 spring.datasource.type 指定数据源。
自己为 com.alibaba.druid.pool.DruidDataSource 绑定全局配置文件中的参数,再添加到容器中,而不再使用 Spring Boot 的自动生成了;我们需要 自己添加 DruidDataSource 组件到容器中,并绑定属性;
创建DruidConfig配置类:
import com.alibaba.druid.support.http.StatViewServlet; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; @Configuration public class DruidConfig { // 将配置文件中默认不能注入的属性值关联配置类 @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druid(){ return new DruidDataSource(); } //配置 Druid 监控管理后台的Servlet; //内置 Servler 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式 //配置Druid的监控 //1、配置一个管理后台的Servlet @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map<String,String> initParams = new HashMap<>(); initParams.put("loginUsername","admin"); //后台管理界面的登录账号 initParams.put("loginPassword","123456"); //后台管理界面的登录密码 //后台允许谁可以访问 //initParams.put("allow", "localhost"):表示只有本机可以访问 //initParams.put("allow", ""):为空或者为null时,表示允许所有访问 initParams.put("allow","");//默认就是允许所有访问 //deny:Druid 后台拒绝谁访问 initParams.put("deny","192.168.15.21"); // 表示禁止此ip访问 //设置初始化参数 bean.setInitParameters(initParams); return bean; } //2、配置一个web监控的filter // 这个过滤器的作用就是统计 web 应用请求中所有的数据库信息,比如 发出的 sql 语句,sql 执行的时间、请 // 求次数、请求的 url 地址、以及seesion 监控、数据库表的访问次数 等等。 @Bean public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); Map<String,String> initParams = new HashMap<>(); //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计 initParams.put("exclusions","*.js,*.css,/druid/*"); bean.setInitParameters(initParams); // "/*" 表示过滤所有请求 bean.setUrlPatterns(Arrays.asList("/*")); return bean; } //这些参数可以在 com.alibaba.druid.support.http.StatViewServlet 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到 }
配置完毕后,我们可以选择访问 : http://localhost:8080/druid/login.html
测试:
发送一条sql语句,然后来看一下后台的消息;
DruidDataSource配置属性列表
DruidDataSource配置兼容DBCP,但个别配置的语意有所区别。
配置 | 缺省值 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this). 另外配置此属性至少在1.0.5版本中是不起作用的,强行设置name会出错。详情-点此处。 | |
url | 连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里 | |
driverClassName | 根据url自动识别 | 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxPoolPreparedStatementPerConnectionSize | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 | |
validationQueryTimeout | 单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
keepAlive | false (1.0.28) | 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。 |
timeBetweenEvictionRunsMillis | 1分钟(1.0.14) | 有两个含义: 1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明 |
numTestsPerEvictionRun | 30分钟(1.0.14) | 不再使用,一个DruidDataSource只支持一个EvictionRun |
minEvictableIdleTimeMillis | 连接保持空闲而不被驱逐的最小时间 | |
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 | 当数据库抛出一些不可恢复的异常时,抛弃连接 |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall | |
proxyFilters | 类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |
整合MyBatis
Springboot没有Mybatis的启动器,所以添加依赖时需要指定版本
spring boot 官方并没有提供 MyBaits 的启动器,是 MyBatis 官方提供的开发包来适配的 spring boot,从 pom.xml 文件中的依赖包名也能看出来,并非是以 spring-boot 开头的;
<!--mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> <!--MySQL--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
配置application.yml配置文件
#配置数据源 spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: # 注意:对应实体类的路径 type-aliases-package: com.myspringboot.pojo # mapper.xml文件位置,mybatis前面不能加“/”加了就到工程目录下找,classpath代表resource目录,如果没有映射文件,请注释掉 # 在resource目录下找mapper.xml映射文件 mapper-locations: classpath:mybatis/mapper/*.xml # 指定全局配置文件的位置 config-location: classpath:mybatis/mybatis-config.xml # 驼峰扫描 configuration: map-underscore-to-camel-case: true
application.yml配置文件中添加mybatis-config.xml
配置文件,在spring的配置文件已经指定了mybatis的配置文件,若修改默认配置,则添加这一配置,不添加则使用spring默认配置。
config-locations: classpath:mybatis/mybatis-config.xml
创建mapper接口:
//指定这是一个操作数据库的mapper @Mapper @Repository public interface DepartmentMapper { @Select("select * from department where id=#{id}") public Department getDeptById(Integer id); @Delete("delete from department where id=#{id}") public int deleteDeptById(Integer id); @Options(useGeneratedKeys = true,keyProperty = "id") // 设置自增主键 @Insert("insert into department(departmentName) values(#{departmentName})") public int insertDept(Department department); @Update("update department set departmentName=#{departmentName} where id=#{id}") public int updateDept(Department department); }
Mapper接口的位置在application.yml中并不能配置,Mapper接口的扫描的实现方式:
在启动类上添加扫描包注解:
@SpringBootApplication @MapperScan({"com.hguo.mapper"}) // 与在mapper接口上使用@mapper注解相同 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
resource目录创建mybatis/mapper/XxxMapper.xml
总结:
1.导入mybatis依赖,配置文件配置数据源、整合mybatis,创建实体类作为映射条件;
2.创建mapper接口,接口上或者启动类上使用注解@Mapper
或@MapperScan("mapper接口位置")
声明是mybatis的mapper类;
3.创建service、controller调用。
配置文件application.yaml中可以配置:
#时间日期格式化 spring: mvc: date-format: yyyy-MM-dd
整合SpringData JPA
Spring Data JPA是Spring Data家族的一部分,可以轻松实现基于JPA的存储库。此模块处理对基于JPA的数据访问层的增强支持。它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。
1)、SpringData简介
2)、整合SpringData JPA
引入mysql和spring-data-jpa依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
创建实体类
JPA:ORM(Object Relational Mapping);
编写一个实体类(bean)和数据表进行映射,并且配置好映射关系;
//使用JPA注解配置映射关系 @Entity //告诉JPA这是一个实体类(和数据表映射的类) @Table(name = "tbl_user") //@Table来指定和哪个数据表对应;如果省略默认表名就是user; public class User { @Id //这是一个主键 @GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键 private Integer id; @Column(name = "last_name",length = 50) //这是和数据表对应的一个列,设置列名 private String lastName; @Column //省略默认列名就是属性名 private String email;
@GeneratedValue是主键生成策略,Jpa自带的几种主键生成策略如下:
- TABLE:使用一个特定的数据库表格来保存主键
- SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。这个值要与generator一起使用,generator 指定生成主键使用的生成器(可能是orcale中自己编写的序列)
- IDENTITY:主键由数据库自动生成(主要是支持自动增长的数据库,如mysql)
- AUTO:主键由程序控制,也是GenerationType的默认值
主键生成策略扩展
自定义主键生成器:
public class MyGenerator implements IdentifierGenerator { @Override public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException { return getId(); } public static long getId(){ return System.currentTimeMillis(); } }
然后在实体类做一下配置:
@Data @NoArgsConstructor @AllArgsConstructor @Entity public class Student implements Serializable { private static final long serialVersionUID = 6712540741269055064L; @Id @GenericGenerator(name="idGenerator",strategy="com.javatrip.springdatajpa.entity.MyGenerator") @GeneratedValue(generator = "idGenerator") private Integer studentId; private Integer age; private String name; private Integer sex; private Date createTime; private Integer status; }
创建dao接口
dao层接口实现JpaRepository,泛型选择pojo和其主键类型,就会自动实现简单的CRUD等接口,无需手动开发,就能快速进行调用了。
编写一个Dao接口来操作实体类对应的数据表(Repository)
//继承JpaRepository来完成对数据库的操作 // 不用加任何注解,本身就是jpa的Repository // 不用写方法 public interface UserRepository extends JpaRepository<User,Integer> { /** * 根据年龄,名字模糊查询 * @return */ Student findByNameLikeAndAge(String name,int age); }
Jpa除了实现CRUD方法,还支持字段名模糊查询等各种不用手写sql的操作。
基本的配置JpaProperties
application.yml:
spring: jpa: hibernate: #更新或者创建数据表结构 ddl-auto: update #控制台显示SQL show-sql: true
测试:
启动启动类,数据库中自动帮我们创建了实体类指定的映射表和表结构。
@SpringBootTest class SpringDataJpaApplicationTests { @Autowired StudentRepository repository; @Test void contextLoads() { // 查询所有实体 List<Student> all = repository.findAll(); // 根据id查询实体类 Optional<Student> byId = repository.findById(100); // 根据id删除数据 repository.deleteById(100); // 插入一条数据 // 如果数据库存在该实体的主键,则更新,否则插入 Student student = new Student(); student.setAge(18); student.setName("Java旅途"); repository.save(student); repository.findByNameLikeAndAge("Java",18); } }
本文来自博客园,作者:Lz_蚂蚱,转载请注明原文链接:https://www.cnblogs.com/leizia/p/17158206.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步