如果Spring Boot整合最基本的JDBC的话,则需要经过如下步骤:
第一步:引入对应的starter、驱动:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> |
第二步:参考官方文档,配置数据库连接信息:
spring.datasource.username=root spring.datasource.password=MyPass@123 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver |
DataSourceConfiguration类
该类会根据不同的properties配置,返回不同的DataSource:
①spring.datasource.tomcat则返回org.apache.tomcat.jdbc.pool.DataSource;
②spring.datasource.hikari则返回com.zaxxer.hikari.HikariDataSource;
③spring.datasource.dbcp2则返回org.apache.commons.dbcp2.BasicDataSource;
④还可以自定义DataSource;
DataSourceInitializerInvoker类
该类实现了ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean接口;
查看InitializingBean.afterPropertiesSet方法:
@Override public void afterPropertiesSet() { DataSourceInitializer initializer = getDataSourceInitializer(); if (initializer != null) { boolean schemaCreated = this.dataSourceInitializer.createSchema(); if (schemaCreated) { initialize(initializer); } } } |
然后调用createSchema()方法:
/** * Create the schema if necessary. * @return {@code true} if the schema was created * @see DataSourceProperties#getSchema() */ boolean createSchema() { List<Resource> scripts = getScripts("spring.datasource.schema", this.properties.getSchema(), "schema"); if (!scripts.isEmpty()) { if (!isEnabled()) { logger.debug("Initialization disabled (not running DDL scripts)"); return false; } String username = this.properties.getSchemaUsername(); String password = this.properties.getSchemaPassword(); runScripts(scripts, username, password); } return !scripts.isEmpty(); } |
然后initialize()调用initSchema()方法:
/** * Initialize the schema if necessary. * @see DataSourceProperties#getData() */ void initSchema() { List<Resource> scripts = getScripts("spring.datasource.data", this.properties.getData(), "data"); if (!scripts.isEmpty()) { if (!isEnabled()) { logger.debug("Initialization disabled (not running data scripts)"); return; } String username = this.properties.getDataUsername(); String password = this.properties.getDataPassword(); runScripts(scripts, username, password); } } |
这里,有2个方法频繁出现:getScripts、runScripts:
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) { if (resources != null) { return getResources(propertyName, resources, true); } String platform = this.properties.getPlatform(); List<String> fallbackResources = new ArrayList<>(); fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql"); fallbackResources.add("classpath*:" + fallback + ".sql"); return getResources(propertyName, fallbackResources, false); }
private void runScripts(List<Resource> resources, String username, String password) { if (resources.isEmpty()) { return; } ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.setContinueOnError(this.properties.isContinueOnError()); populator.setSeparator(this.properties.getSeparator()); if (this.properties.getSqlScriptEncoding() != null) { populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name()); } for (Resource resource : resources) { populator.addScript(resource); } DataSource dataSource = this.dataSource; if (StringUtils.hasText(username) && StringUtils.hasText(password)) { dataSource = DataSourceBuilder.create(this.properties.getClassLoader()) .driverClassName(this.properties.determineDriverClassName()).url(this.properties.determineUrl()) .username(username).password(password).build(); } DatabasePopulatorUtils.execute(populator, dataSource); } |
在createSchema的时候,getScripts方法的fallback=schema;
在initSchema的时候,getScripts方法的fallback=data;
再结合上面的红色、加粗字体,我们会发现如下情况:
classpath*:schema-${platform}.sql
classpath*:schema.sql
classpath*:data-${platform}.sql
classpath*:data.sql
其中的platform是来自于spring.datasource.platform配置,默认值为all,可以查看官网说明文档来从侧面证明我们的推测:
classpath:与classpath*:区别
classpath:表示只在指定的class路径中查找文件。如果有多个加载第一个。
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找,有多少加载多少。
如何整合druid?
这个druid是阿里的产品,主要是提供了查询、监测等一系列服务,所以,这里才会选用该产品;
第一步:打开阿里仓库,找到druid的<dependency/>
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.9</version> </dependency> |
第二步:在application.yml中配置属性信息;
想知道属性到底有哪些,则可以参考com.alibaba.druid.pool.DruidDataSource类,下面是该类的属性列表:
|
下面则是application.yml配置文件信息:
spring: datasource: username: root password: MyPass@123 url: jdbc:mysql://127.0.0.1:3306/spring?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM orginfo testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true filters: stat,wall,log4j2 maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 |
第三步:由于很多属性并不属于Spring中的属性,为了能够让这些属性起作用,就必须让我们自己来创建DataSource,通过手动创建来加载只属于DruidDataSource的属性并让属性生效;
第四步:为了让druid的监控统计功能生效,我们必须配置druid提供的servlet、filter;
@Configuration public class DruidConfig { @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druid(){ // 配置druid数据源,这个bean名字可以随便取,不影响后面 return new DruidDataSource(); } // 配置druid监控,需要配置一个管理后台的servlet,一个监控的filter @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean servlet = new ServletRegistrationBean( new com.alibaba.druid.support.http.StatViewServlet(), "/druid/*" ); Map<String,String> initParam = new HashMap<>(); initParam.put("loginUsername","root");// 配置用户名 initParam.put("loginPassword","root");// 配置密码 initParam.put("allow","");// 配置可以访问的IP地址,这里是允许所有IP访问 servlet.setInitParameters(initParam); return servlet; } @Bean public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean filter = new FilterRegistrationBean(); filter.setFilter(new com.alibaba.druid.support.http.WebStatFilter()); Map<String,String> initParam = new HashMap<>(); initParam.put("exclusions","*.js,*.css,*.jpg,*.png,/druid/*"); filter.setInitParameters(initParam); filter.setUrlPatterns(Arrays.asList(new String[]{"/*"})); return filter; } } |
最后,查看效果:
如何整合Mybatis(注解版)
在整合了druid的基础上,继续整合Mybatis:
第一步:在POM中添加mybatis的启动器(自己去阿里maven上去搜即可):
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> |
第二步:基于@Mapper编写接口:
@Mapper public interface OrgInfoMapper {
@Update("update orginfo set orgname = #{orgname},areacode = #{areacode} where orgcode = #{orgcode}") public int updateOrgInfo(OrgInfo orgInfo);
@Insert("insert into orginfo(orgcode,orgname,areacode) values(#{orgcode},#{orgname},#{areacode})") public int insertOrgInfo(OrgInfo orgInfo);
@Select("select * from orginfo where orgcode = #{orgcode}") public OrgInfo getOrgInfoById(String orgcode);
@Delete("delete from orginfo where orgcode = #{orgcode}") public int deleteOrgInfoById(String orgcode); } |
第三步:编写@Controller,这里图方便,直接使用@RestController:
@RestController public class OrgInfoRestController {
@Autowired private OrgInfoMapper orgInfoMapper;
@GetMapping("/orginfo/{id}") public OrgInfo getOrgInfoById( @PathVariable String id){ System.out.println("id = " + id); return orgInfoMapper.getOrgInfoById(id); } } |
查看访问效果:
查看druid监控效果:
如何整合Mybatis(把注解版改造成XML版)
第一步:改造OrgInfoMapper类,把注解@Mapper、@Insert、Delete、Update、Select全部去掉;
把OrgInfoMapper类的全类名作为SQL映射文件<mapper/>的namespace属性值;
方法名分别作为<select、update、insert、delete/>的id属性值;
<?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.springboot.web.mapper.OrgInfoMapper"> <select id="getOrgInfoById" resultType="com.springboot.web.entities.OrgInfo"> select * from orginfo where orgcode = #{orgcode} </select>
<update id="updateOrgInfo" > update orginfo set orgname = #{orgname},areacode = #{areacode} where orgcode = #{orgcode} </update>
<insert id="insertOrgInfo" > insert into orginfo(orgcode,orgname,areacode) values(#{orgcode},#{orgname},#{areacode}) </insert>
<delete id=""> delete from orginfo where orgcode = #{orgcode} </delete> </mapper> |
//@Mapper public interface OrgInfoMapper {
// @Update("update orginfo set orgname = #{orgname},areacode = #{areacode} where orgcode = #{orgcode}") public int updateOrgInfo(OrgInfo orgInfo);
// @Insert("insert into orginfo(orgcode,orgname,areacode) values(#{orgcode},#{orgname},#{areacode})") public int insertOrgInfo(OrgInfo orgInfo);
// @Select("select * from orginfo where orgcode = #{orgcode}") public OrgInfo getOrgInfoById(String orgcode);
// @Delete("delete from orginfo where orgcode = #{orgcode}") public int deleteOrgInfoById(String orgcode); } |
第二步:配置mybatis全局文件:
mybatis: config-location: classpath:mybatis/mybatis-config.xml #指定全局配置文件路径 mapper-locations: classpath:mybatis/mapper/*.xml #指定SQL映射文件路径 |
第三步:其他不用修改;
效果:
查看druid监控效果:
注意:注解配置方式与XML配置方式是共存的,而不是互斥的,两种方式可以一起使用;
Mybatis拓展
先看一下MybatisAutoConfiguration的类声明:
@Configuration @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}) public class MybatisAutoConfiguration implements InitializingBean {
} |
再看看MybatisProperties的类声明:
@ConfigurationProperties( prefix = "mybatis" ) public class MybatisProperties {
} |
这里我们可以得知,mybatis的属性配置都是以“mybatis”开头的;
public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) { this.properties = properties; this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable(); this.typeHandlers = (TypeHandler[])typeHandlersProvider.getIfAvailable(); this.languageDrivers = (LanguageDriver[])languageDriversProvider.getIfAvailable(); this.resourceLoader = resourceLoader; this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable(); this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable(); }
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { ...... this.applyConfiguration(factory); ...... }
private void applyConfiguration(SqlSessionFactoryBean factory) { 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)) { Iterator var3 = this.configurationCustomizers.iterator();
while(var3.hasNext()) { ConfigurationCustomizer customizer = (ConfigurationCustomizer)var3.next(); customizer.customize(configuration); } } factory.setConfiguration(configuration); } |
这里提到一个变量:List<ConfigurationCustomizer> configurationCustomizers;这里有个特殊地方在于:
import org.apache.ibatis.session.Configuration; import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer; import org.springframework.context.annotation.Bean;
@org.springframework.context.annotation.Configuration public class MyBatisConfig { /** * org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer * org.apache.ibatis.session.Configuration * */ @Bean public ConfigurationCustomizer configurationCustomizer(){ return new ConfigurationCustomizer(){ @Override public void customize(Configuration configuration) { /** * 如果数据库某个字段修改了,可以使用这个属性 * 例如:orgname 改成了 org_name * 这种情况下,通过setMapUnderscoreToCamelCase属性就可以不用修改Bean属性的情况下 * 来解决这种类型的改动 * */ configuration.setMapUnderscoreToCamelCase(true); } }; } } |
通过自定义ConfigurationCustomizer,并添加到Spring容器中,这样就可以把自定义的ConfigurationCustomizer放入到configurationCustomizers变量中;
最后就是mybatis自身提供的@Mapper、@MapperScan、@MapperScans注解;
如何整合JPA
JPA是ORM框架(Object-Relational-Mapping)的一种,要想使用该框架,一共有5个步骤:
第一步:使用JPA的starter:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> |
第二步:声明Entity与Table:
@Entity @Table(name = "user_info") public class Userinfo { /** * 下面的注解,属于JPA独有的注解,潜台词就是告诉Spring Boot,下面要使用JPA了 * * @Entity标明该类是一个与数据表进行映射的实体类 * @Table(name = "user_info")标明该类要映射的数据表名称是user_info * * @Id标明该属性是一个主键 * @GeneratedValue(strategy = GenerationType.IDENTITY)标明该属性具有自增属性 * @Column标明该属性要映射的数据表的column */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id;
@Column(name = "cert_name", length = 50) private String certName;
@Column(name = "cert_id") private String certId;
@Override public String toString() { return "Userinfo{" + "id=" + id + ", certName='" + certName + '\'' + ", certId='" + certId + '\'' + '}'; }
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getCertName() { return certName; }
public void setCertName(String certName) { this.certName = certName; }
public String getCertId() { return certId; }
public void setCertId(String certId) { this.certId = certId; } } |
第三步:声明Repository:
public interface UserInfoRepository extends JpaRepository<Userinfo,Integer> { /** * JpaRepository泛型的第一个参数指的是数据表对应的实体类 * 第二个参数则是实体类对应的主键 * 可以查看源代码下面的方法就能看出来: * T getOne(ID var1); * <S extends T> List<S> findAll(Example<S> var1); * */ } |
第四步:配置application.yml文件:
Spring: jpa: hibernate: ddl-auto: update # 标明当实体类发生变化的时候,对应的数据表自动进行相应变化,表没有的时候创建,创建完了,除了进行修改也不再动了 show-sql: true #显示SQL语句 |
第五步:编写controller:
@RestController public class OrgInfoRestController {
@Autowired private OrgInfoMapper orgInfoMapper;
@Autowired UserInfoRepository userInfoRepository;
@GetMapping("/orginfo/{id}") public OrgInfo getOrgInfoById( @PathVariable String id){ return orgInfoMapper.getOrgInfoById(id); }
@GetMapping("/userinfo/{id}") public Userinfo getUserInfoById( @PathVariable Integer id){ Optional<Userinfo> userinfo = userInfoRepository.findById(id); return userinfo.get(); }
@GetMapping("/userinfo") public Userinfo insertUserInfoById(Userinfo userinfo){ return userInfoRepository.save(userinfo); } } |
查看效果:
查看druid监控效果: