06-数据访问(上)
2021/9/14 23:09:51
本文主要是介绍06-数据访问(上),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1. 数据源的自动配置
1.1 导入 JDBC 场景
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency>
查看依赖:
为什么导入 JDBC 场景,官方不导入驱动?因为官方不知道我们接下要操作什么数据库。
<properties> ... <mysql.version>5.1.49</mysql.version> </properties> <dependencies> <!-- 版本控制依赖的是 8.x.x,但本机上的数据库是 5.7.x,↑ ↓ 修改依赖版本的两种方式 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <!-- <version>5.1.49</version> --> </dependency> ... <dependencies>
1.2 相关自动配置
DataSourceAutoConfiguration(数据源的自动配置类)
@ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements ... { ... } // ·········································································· @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") // 响应式编程 @EnableConfigurationProperties(DataSourceProperties.class) @Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class }) public class DataSourceAutoConfiguration { @Configuration(proxyBeanMethods = false) @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration {} // ... }
数据库连接池的配置,须在容器中没有 DataSource 的情况下才会自动配置;查看上面场景依赖可知,底层配置好的连接池是 Hikari。
修改数据源的配置信息:
spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver # type: com.zaxxer.hikari.HikariDataSource
补充:
- DataSourceTransactionManagerAutoConfiguration(事务管理器的自动配置)
- XADataSourceAutoConfiguration(分布式事务相关的自动配置)
2. 使用 Druid 数据源
整合第三方技术的两种方式:① 自定义√;② starter
2.1 自定义方式
2.1.1 配置数据源
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.17</version> </dependency>
虽然 data-jdbc 场景默认导入了 Hikari 数据源,但是可以看到 DataSourceConfiguration 类中的各种数据源配置上都有 @ConditionalOnMissingBean(DataSource.class)
这个条件,所以我们只需自己在配置类里配置一个 DruidDataSource,Hikari 就不会生效了。
@Configuration public class DaoConfig { @Bean @ConfigurationProperties("spring.datasource") public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); return dataSource; } }
2.1.2 监控统计功能
打开Druid的监控统计功能&使用Druid的内置监控页面
(1)Druid 内置提供一个 StatFilter,用于统计监控信息。
StatFilter 的别名是 stat,这个别名映射配置信息保存在 druid-xxx.jar!/META-INF/druid-filter.properties 中。在 Spring 中使用别名配置方式如下:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" ...> ... <property name="filters" value="stat" /> </bean>
转换成 SpringBoot 的写法:① 在核心配置文件中配置 spring.datasource.filters=stat
以绑定到 DruidDataSource 上;② 配置数据源的时候直接 set 进去,如下所示。
@Bean @ConfigurationProperties("spring.datasource") public DataSource dataSource() throws SQLException { DruidDataSource dataSource = new DruidDataSource(); dataSource.setFilters("stat"); return dataSource; }
(2)Druid 内置提供了一个 StatViewServlet 用于展示 Druid 的统计信息。
这个 StatViewServlet 的用途包括:① 提供监控信息展示的 html 页面;② 提供监控信息的 JSON API。
根据配置中的 url-pattern 来访问内置监控页面,如果是下面的配置,内置监控页面的首页是 /druid/index.html。
<servlet> <servlet-name>DruidStatView</servlet-name> <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DruidStatView</servlet-name> <url-pattern>/druid/*</url-pattern> </servlet-mapping>
转换成 SpringBoot 的写法:
@Bean public ServletRegistrationBean statViewServlet() { StatViewServlet servlet = new StatViewServlet(); ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(servlet, "/druid/*"); return bean; }
如果不想该页面直接被访问,可以配置监控页面访问密码(loginUsername&loginPassword),还可以配置 allow 和 deny(访问 IP)。
(3)WebStatFilter 用于采集 web-jdbc 关联监控的数据(详见上面的超链接)。
2.2 starter 方式
https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
2.2.1 导入 starter
注掉 druid 的依赖,引入 druid-starter 的依赖:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency>
2.2.2 自动配置类
@Configuration @ConditionalOnClass(DruidDataSource.class) // before! 不然就先创建 Hikari 了! @AutoConfigureBefore(DataSourceAutoConfiguration.class) @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) @Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class DruidDataSourceAutoConfigure { @Bean(initMethod = "init") @ConditionalOnMissingBean public DataSource dataSource() { LOGGER.info("Init DruidDataSource"); return new DruidDataSourceWrapper(); } }
2.2.3 配置类
(1)DruidStatProperties
@ConfigurationProperties("spring.datasource.druid") public class DruidStatProperties { private String[] aopPatterns; private StatViewServlet statViewServlet = new StatViewServlet(); private WebStatFilter webStatFilter = new WebStatFilter(); public static class StatViewServlet { /** * Enable StatViewServlet, default false. */ private boolean enabled; private String urlPattern; private String allow; private String deny; private String loginUsername; private String loginPassword; private String resetEnable; } public static class WebStatFilter { /** * Enable WebStatFilter, default false. */ private boolean enabled; private String urlPattern; private String exclusions; private String sessionStatMaxCount; private String sessionStatEnable; private String principalSessionName; private String principalCookieName; private String profileEnable; } }
(2)DruidSpringAopConfiguration
@ConditionalOnProperty("spring.datasource.druid.aop-patterns") public class DruidSpringAopConfiguration { ... }
(3)DruidStatViewServletConfiguration
@ConditionalOnWebApplication @ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true") public class DruidStatViewServletConfiguration { ... }
(4)DruidWebStatFilterConfiguration
@ConditionalOnWebApplication @ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true") public class DruidWebStatFilterConfiguration { ... }
(5)DruidFilterConfiguration
public class DruidFilterConfiguration { @Bean @ConfigurationProperties(XXX) @ConditionalOnProperty(prefix = XXX, name = "enabled") @ConditionalOnMissingBean public XXX xXX() { return new XXX(); } private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat"; private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config"; private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding"; private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j"; private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j"; private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2"; private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log"; private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall"; private static final String FILTER_WALL_CONFIG_PREFIX = FILTER_WALL_PREFIX + ".config"; }
2.2.4 配置示例
spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: root driver-class-name: com.mysql.jdbc.Driver druid: aop-patterns: cn.edu.nuist.admin.* # 监控 SpringBean filters: stat,wall # 底层开启功能: stat(sql监控)、wall(防火墙) stat-view-servlet: # 配置监控页功能 enabled: true login-username: admin login-password: admin resetEnable: false web-stat-filter: # 监控 web enabled: true urlPattern: /* exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' filter: stat: # 对上面 filters 中开启的 StatFilter 的详细配置 slow-sql-millis: 1000 logSlowSql: true enabled: true wall: # 配置 WallFilter enabled: true config: drop-table-allow: false
3. 整合 MyBatis
https://github.com/mybatis/spring-boot-starter
3.1 导入依赖&自动配置
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency>
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX) public class MybatisProperties { public static final String MYBATIS_PREFIX = "mybatis"; // ... } // ································································· @org.springframework.context.annotation.Configuration @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties(MybatisProperties.class) @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) public class MybatisAutoConfiguration implements InitializingBean { private static final Logger logger; private final MybatisProperties properties; private final Interceptor[] interceptors; private final TypeHandler[] typeHandlers; private final LanguageDriver[] languageDrivers; private final ResourceLoader resourceLoader; private final DatabaseIdProvider databaseIdProvider; private final List<ConfigurationCustomizer> configurationCustomizers; @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { ... } @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); if (executorType != null) { return new SqlSessionTemplate(sqlSessionFactory, executorType); } else { return new SqlSessionTemplate(sqlSessionFactory); } } // ... /** * This will just scan the same base package as Spring Boot does. If you want more power, * you can explicitly use {@link org.mybatis.spring.annotation.MapperScan} but this will * get typed mappers working correctly. */ public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ... } } /** * If mapper registering configuration or mapper scanning configuration not present, * this configuration allow to scan mappers based on the same component-scanning path * as Spring Boot itself. */ @org.springframework.context.annotation.Configuration @Import(AutoConfiguredMapperScannerRegistrar.class) @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class }) public static class MapperScannerRegistrarNotFoundConfiguration ... }
3.2 简单整合示例
http://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/
CityMapper
@Mapper // 若觉得在每个 Mapper 接口上写注解麻烦,可以直接在启动类上增加 @MapperScan public interface CityMapper { City getCityById(Long id); @Insert("INSERT INTO city(`name`,`state`,`country`) VALUES (#{name},#{state},#{country})") @Options(useGeneratedKeys = true, keyProperty = "id") boolean insertCity(City city); }
CityMapper.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="cn.edu.nuist.mapper.CityMapper"> <select id="getCityById"> SELECT * FROM city WHERE id = #{id} </select> </mapper>
application.yml
# mybatis.configuration(推荐) 和 mybatis.config-location 不能同时存在,全局配置要不然就 # 放全局配置文件中,要不就用 mybatis.configuration.xxx的方式挨个配好,不能混用。 mybatis: mapper-locations: classpath:mybatis.mapper/*.xml configuration: map-underscore-to-camel-case: true
4. 整合 MyBatisPlus
https://baomidou.com/guide/quick-start.html
4.1 导入依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency>
只需要引入一个 MP,mybatis-starter 和 jdbc-starter 就都不用再引了。
4.2 整合测试
(1)实体类 UserPlus
@Data @TableName("user") // 默认找 `user_plus` 表 public class UserPlus { private Long id; private String name; private Integer age; private String email; @TableField(exist = false) private String future; }
(2)映射文件
因为 MybatisPlusProperties 已经有 String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"}
的默认配置,所以在 application.yml 中就没弄任何关于 mybatis / mybatis-plus 开头的配置,就把 *Mapper.xml 的外层目录由 resource/mybatis/mapper 改为 resource/mapper,简单整合嘛这不~
(3)映射文件绑定的接口(没加 @Mapper
注解,直接在主启动类上加 @MapperScan("cn.edu.nuist.mapper")
一次搞定)
public interface UserPlusMapper extends BaseMapper<UserPlus> {}
(4)Service 接口
public interface UserPlusService extends IService<UserPlus> {}
(5)Service 实现
@Service public class UserPlusServiceImpl extends ServiceImpl<UserPlusMapper, UserPlus> implements UserPlusService {}
(6)分页插件
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); return interceptor; }
(7)Controller
@GetMapping("/user/delete/{id}") public String delUser(@RequestParam(value = "pn", defaultValue = "1") Integer pn, @PathVariable("id") Long id, RedirectAttributes ra ) { userPlusService.removeById(id); ra.addAttribute("pn", pn); return "redirect:/dynamic_table"; } @GetMapping("/user/table") public String users(@RequestParam(value="pn", defaultValue="1") Integer pn, Model model) { Page<UserPlus> page = new Page<>(pn, 3); userPlusService.page(page, null); model.addAttribute("users", page); return "table/dynamic_table"; }
这篇关于06-数据访问(上)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-04-26敏捷开发:想要快速交付就必须舍弃产品质量?
- 2024-04-26静态代码分析的这些好处,我竟然都不知道?
- 2024-04-26你在测试金字塔的哪一层?(下)
- 2024-04-26快刀斩乱麻,DevOps让代码评审也自动起来
- 2024-04-262024年最好用的10款ER图神器!
- 2024-04-2203-为啥大模型LLM还没能完全替代你?
- 2024-04-2101-大语言模型发展
- 2024-04-17基于SpringWeb MultipartFile文件上传、下载功能
- 2024-04-14个人开发者,Spring Boot 项目如何部署
- 2024-04-14RAG应用开发实战02-相似性检索的关键 - Embedding