1. 简介
- 对于DAO层,SQL or NoSQL,Spring Boot默认在底层使用 Spring Data 的方式进行统一处理,通过各种xxxTemplate,xxxRepository简化对DAO层的操作。
- 与DAO层有关的starter,使用spring-boot-starter-data-{数据交互产品}进行命名如spring-boot-starter-data-jdbc等。
2.整合原生JDBC(需要导入jdbc模块和对应数据库驱动模块)
2.1 简单配置(映射绑定的是DataSourceProperties)
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: sysadmin
url: jdbc:mysql://localhost:3306/mybatis
效果:默认使用 org.apache.tomcat.jdbc.pool.DataSource
@ConfigurationProperties(
prefix = "spring.datasource"
)
public class DataSourceProperties implements BeanClassLoaderAware, EnvironmentAware, InitializingBean {
private ClassLoader classLoader;
private Environment environment;
private String name = "testdb";
private boolean generateUniqueName;
private Class<? extends DataSource> type;
private String driverClassName;
private String url;
private String username;
private String password;
private String jndiName;
private boolean initialize = true;
private String platform = "all";
private List<String> schema;
private String schemaUsername;
private String schemaPassword;
private List<String> data;
private String dataUsername;
private String dataPassword;
private boolean continueOnError = false;
private String separator = ";";
private Charset sqlScriptEncoding;
private EmbeddedDatabaseConnection embeddedDatabaseConnection;
private DataSourceProperties.Xa xa;
private String uniqueName;
//... ...
}
2.2. 数据源自动配置原理(相关类都在org.springframework.boot.autoconfigure.jdbc下)
-
参考DataSourceConfiguration,根据@ConditionalOnClass即导入的依赖情况和@ConditionalOnProperty(spring.datasource.type)来选择创建哪种数据源,默认使用Tomcat连接池。
(1)Spring Boot默认支持:org.apache.tomcat.jdbc.pool.DataSource/HikariDataSource/BasicDataSource(dbcp和dbcp2)。
(2)自定义数据源的原理@ConditionalOnMissingBean({DataSource.class}) @ConditionalOnProperty( name = {"spring.datasource.type"} ) static class Generic { Generic() { } @Bean public DataSource dataSource(DataSourceProperties properties) { //建造者模式创建数据源,利用BeanUtil反射创建相应Type的数据源,并绑定相关属性 return properties.initializeDataSourceBuilder().build(); } }
-
DataSourceAutoConfiguration向容器中添加了一个 DataSourceInitializer
(implements ApplicationListener),其作用包括:
(1) runSchemaScripts():应用启动时运行建表语句脚本,这些文件命名模式为schema-*.sql,默认为schema-all.sql。(注意,每次重启时都会删掉同名旧表创建新表。第二次启动前应该删掉这段配置。)private void runSchemaScripts() { List<Resource> scripts = this.getScripts("spring.datasource.schema", this.properties.getSchema(), "schema"); if (!scripts.isEmpty()) { String username = this.properties.getSchemaUsername(); String password = this.properties.getSchemaPassword(); this.runScripts(scripts, username, password); try { this.applicationContext.publishEvent(new DataSourceInitializedEvent(this.dataSource)); if (!this.initialized) { this.runDataScripts(); this.initialized = true; } } catch (IllegalStateException var5) { logger.warn("Could not send event to complete DataSource initialization (" + var5.getMessage() + ")"); } } private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) { if (resources != null) { return this.getResources(propertyName, resources, true); } else { String platform = this.properties.getPlatform(); List<String> fallbackResources = new ArrayList(); //fallback是传递过来的“schema” //private String platform = "all";故schema-all.sql/schema.sql都是默认加载的 fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql"); fallbackResources.add("classpath*:" + fallback + ".sql"); return this.getResources(propertyName, fallbackResources, false); } }
(2) runDataScripts():运行插入数据的语句脚本,这些文件命名模式为data-*.sql,默认为data-all.sql。
private void runDataScripts() { List<Resource> scripts = this.getScripts("spring.datasource.data", this.properties.getData(), "data"); String username = this.properties.getDataUsername(); String password = this.properties.getDataPassword(); this.runScripts(scripts, username, password); }
(3)如果不想用默认值,可以在配置文件中指定spring: datasource: schema 可以指定自定义sql脚本位置。
spring: datasource: #... #schema是一个List<String> schema: - classpath:department.sql - classpath:sql/employee.sql
-
JdbcTemplateAutoConfiguration自动配置JdbcTemplate来操作数据库。
3.使用Druid数据源
- 在pom.xml中导入Druid依赖。
- 在主配置文件中指定数据源type:com.alibaba.druid.pool.DruidDataSouce及其他初始化参数:
spring: datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: sysadmin url: jdbc:mysql://localhost:3306/mybatis type: com.alibaba.druid.pool.DruidDataSource initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true
- 由于主配置文件中前缀为spring.datasource的配置默认映射绑定的是DataSourceProperties,而其中并没有Druid的相关设置,就无法映射绑定这些属性。 因此,只能手动在配置类@Configuration中通过@Bean注入Druid数据源。
@Configuration public class DruidConfig { //能这样使用@ConfigurationProperties是因为DruidDataSource中声明了这些属性,就能够一一映射绑定。 //同时这种用法从另一个角度说明了,只要是注入到容器中的组件,不论是它作为属性、返回值,都可以使用@ConfigurationProperties进行映射绑定。 @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druid(){ return new DruidDataSource(); }
- 在配置类@Configuration中注入管理后台的Servlet和监控的Filter。
//配置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"); bean.setInitParameters(initParams); return bean; } //2.配置一个监控的Filter @Bean public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); //设置初始化参数 Map<String,String> initParams=new HashMap<>(); initParams.put("exclusions","*.js,*.css,/druid/*"); bean.setUrlPatterns(Arrays.aslist("/*")); bean.setInitParameters(initParams); return bean; } }
4. 整合MyBatis(需要导入对应数据库驱动模块和MyBatis模块)
4.1 mybatis-spring-boot-starter
- MyBatis模块的mybatis-spring-boot-starter不是Spring官方出品的,而是MyBatis官方自己对SpringBoot的适配。
由mybatis-spring-boot-starter引入的依赖有spring-boot-starter-jdbc、mybatis(本体包)、mybatis-spring(mybatis与spring的整合包)、mybatis-spring-boot-autoconfigure(自动配置包)。<!--pom.xml--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
4.2 全注解方式使用MyBatis
- 重要注解@Mapper(一个个标注在Mapper接口上)/@MapperScan(一次性标注在配置类上,批量扫描)
//说明这是一个Mapper,如果没有主配置类/MyBatis配置类上没有标注@MapperScan,则每个Mapper上必须标注@Mapper @Mapper public interface DepartmentMapper { @Select("SELECT * FROM tbl_dept WHERE id=#{id}") public Department getDeptById(Integer id); @Delete("DELETE FROM tbl_dept WHERE id=#{id}") public Integer deleteDeptById(Integer id); //获取自增主键。在插入后,主键被封装到参数dept中 @Options(useGeneratedKeys=true,keyProperty="id") @Insert("INSERT INTO tbl_dept(departmentName) values(#{departmentName})") public Integer insertDept(Department dept); @Update("UPDATE tbl_dept SET departmentName=#{departmentName} WHERE id=#{id}") public Integer updateDept(Department dept); }
- MyBatis自动配置原理(MyBatisAutoConfiguration+MyBatisProperties)
@Configuration @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnBean({DataSource.class}) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class}) public class MybatisAutoConfiguration { //... ... private final MybatisProperties properties; @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration(); if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { configuration = new org.apache.ibatis.session.Configuration(); } if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { //获得容器中所有的ConfigurationCustiomizer,遍历调用其customize方法,获得自定义MyBatis相关设置。 Iterator var4 = this.configurationCustomizers.iterator(); while(var4.hasNext()) { ConfigurationCustomizer customizer = (ConfigurationCustomizer)var4.next(); customizer.customize(configuration); } } factory.setConfiguration(configuration); if (this.properties.getConfigurationProperties() != null) { factory.setConfigurationProperties(this.properties.getConfigurationProperties()); } if (!ObjectUtils.isEmpty(this.interceptors)) { factory.setPlugins(this.interceptors); } if (this.databaseIdProvider != null) { factory.setDatabaseIdProvider(this.databaseIdProvider); } if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); } if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } return factory.getObject(); } //... ... }
//主配置文件中,对MyBatis的所有配置都以mybatis前缀开头。 @ConfigurationProperties( prefix = "mybatis" ) public class MybatisProperties { public static final String MYBATIS_PREFIX = "mybatis"; private String configLocation; private String[] mapperLocations; private String typeAliasesPackage; private String typeHandlersPackage; private boolean checkConfigLocation = false; private ExecutorType executorType; private Properties configurationProperties; //... ... }
- 自定义MyBatis的配置,除了在主配置文件中设置mybatis.xxx外,还可以在配置类中给容器注入一个ConfigurationCustomizer。
//批量扫描所有Mapper接口 @MapperScan(value="me.zj.springboot.mapper") @Configuration public class MyBatisConfig { @Bean public ConfigurationCustomizer configurationCustomizer(){ return new ConfigurationCustomizer() { @Override public void customize(org.apache.ibatis.session.Configuration configuration) { //驼峰映射 configuration.setMapUnderscoreToCamelCase(true); } }; } }
4.2 配置文件(MyBatis全局配置文件+Mapper映射文件)方式使用MyBatis
- 这种方式同样需要标注@Mapper或@MapperScan。
- 与之前的应用只有一点不同需要注意:在主配置文件中指定mybatis.config-location(mybatis的全局配置文件)和mybatis.mapper-locations(mapper与sql的映射文件)。
4.3 全注解与全配置的方式可以结合使用
只要在主配置文件指定mybatis.mapper-locations即可使用配置文件的方式映射Mapper接口,而不是@Select/@Insert/@Update/@Delete等注解。