上一节我们专门针对于SpringBoot的数据源的使用做了完整的讲解,尤其是对DruidDataSource的使用做了详细的分析,其实上节我们手写的DruidAutoConfig配置类,Alibaba官方已经给到了相应的启动jar包,包含这个配置类,根本不需要我们自己去写… 哈哈,并不是不想提前告诉你们,毕竟了解实现的原理最重要嘛!
那么本节呢,咱们换一种使用方式,并集成JdbcTemplate来尝试获取数据库中的数据吧!
我们先在pom.xml中加入Druid的 “启动装置”
重新Update我们的项目之后看,在Maven Dependencies可以找到刚刚下好的druid-spring-boot-starter-1.1.18.jar
欸?好像发现一个很熟悉的类,DruidDataSourceAutoConfigure不正是我们上节手写的DruidAutoConfigure配置类吗?好奇心驱使着我打开了DruidDataSourceAutoConfigure的源码
第一眼就看到了作者lihengming,我发现阿里的大佬儿们,完成一个项目之后都会把自己邮箱给备注在上面,哈哈。回到正题,这第一页就这几行代码,我们先看注解部分,源码中也在方法上添加了@Bean(initMethod = "init")
,这个属性用于在bean初始化时指定执行方法,主要是为了替代继承 InitializingBean接口。
类上的注解不用我解释,相信大家也应该有经验知道是啥意思了,不难看出我们配置类是必须依赖DruidDataSource数据源的,咱们点开DruidStatProperties可以看到有哪些druid属性可以在配置文件中配置。
另外@ConditionalOnMissingBean
指的就是当IoC容器中不存在DataSource数据源时,才会执行。
其他的就没啥好说的啦,咱们回过头来看看下面这个方法
public DataSource dataSource() {
LOGGER.info("Init DruidDataSource");
return new DruidDataSourceWrapper();
}
该方法就只做了一件事情,返回了DruidDataSourceWrapper对象?根据这个类的名字不难推断这应该是一个DruidDataSource的包装类,那么我们接着点开这个类的源码瞧瞧。
@ConfigurationProperties("spring.datasource.druid")
class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {
@Autowired
//自动装配DataSourceProperties文件类
private DataSourceProperties basicProperties;
@Override
public void afterPropertiesSet() throws Exception {
//if not found prefix 'spring.datasource.druid' jdbc properties ,'spring.datasource' prefix jdbc properties will be used.
//当我们在配置文件中没有找到以'spring.datasource.druid'开头的"头信息"时
//默认加载'spring.datasource'中的配置信息
if (super.getUsername() == null) {
super.setUsername(basicProperties.determineUsername());
}
if (super.getPassword() == null) {
super.setPassword(basicProperties.determinePassword());
}
if (super.getUrl() == null) {
super.setUrl(basicProperties.determineUrl());
}
if (super.getDriverClassName() == null) {
super.setDriverClassName(basicProperties.getDriverClassName());
}
}
@Autowired(required = false)
public void autoAddFilters(List<Filter> filters){
//将filter放入过滤器链中,这里的filter中应当存在WebStatFilter
super.filters.addAll(filters);
}
}
从这里可以看出,DruidDataSourceWrapper确实就是将DruidDataSource稍微包装了一下,当我们在配置文件中没有找到以spring.datasource.druid
开头的"头信息"的时侯,SpringBoot默认会去加载默认会去加载spring.datasource
中的配置信息,咱们之前也在配置类上定义过spring.datasource.druid
对吧?另外除了数据源配置的4大要素不能为空以外,其他的信息默认是可以不用配置的。
在这里我们就不再继续深入研究里面的东西了,底层和我们上节手写的代码是差不多的,只不过细节方面会做的更好。
接下来就是配置yml文件的配置,其实也和上节咱们写的配置差不多,只不过在druid的starter包中将数据源的一些基本配置要素都放进了spring.datasource.druid中,具体可以参考配置文件类DruidStatProperties
spring:
datasource:
druid:
url: jdbc:mysql://127.0.0.1:3306/user
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: zcq774798947
max-active: 20 #最大活跃连接量
initial-size: 5 #初始连接数量
max-wait: 5000 #最大等待量
validation-query: select 'x' #配合定期检查的sql,每隔一段时间检查sql是否正常
keep-alive: true #定期检查开启
enable: true #启用,默认为false
## 监控页面的配置
filters: stat
stat-view-servlet:
login-username: root
login-password: zcq774798947
allow:
deny:
enabled: true #启用,默认为false
url-pattern: /druid/*
web-stat-filter:
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
enabled: true #启用,默认为false
profile-enable: true
session-stat-enable: true
principal-session-name: USER_SESSION
principal-cookie-name: USER_COOKIE
url-pattern: /druid/*
接下来咱们集成JdbcTemplate,看看能否使用DruidDataSource数据源操作数据库。
大家还记得在讲解SpringBoot启动原理的那一章提到的存放118名 “大将” 的文件spirng.factories吗?咱们来点开看看… 发现我们JdbcTemplateAutoConfiguration也在这118名 “大将” 中,而且我们也到导入了spring-jdbc的jar包
那么根据我们以前的经验,我猜想JdbcTemplate在SpringBoot中是可以直接使用的,到底是不是这样呢?我们来测试看看吧~
package com.marco;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootDatasourceApplicationTests {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void contextLoads() {
Integer count = jdbcTemplate.queryForObject("select (1) from userinfo", Integer.class);
System.out.println(count);
}
}
我靠!!怎么报了这么长的错误,抛出的异常说The server time zone value '?й???ʱ?' is unrecognized
,还有乱码?
既然问题抛出来了,咱们就要解决吗,根据异常显示,大概可以猜出来是timezone的问题,You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
根据抛出的异常后面的内容,我们可以略加分析一下,这句英文的大意是 “如果你想使用timezone,必须定义更加详细确切的timezone时区”,我们想想到底哪个地方用了timezone啊?
记性好的朋友应该还记得我在上节 Marco’s Java【SpringBoot进阶(三) 之 SpringBoot数据源配置和自动管理】 有提到因为我的mysql驱动mysql-connector-java
是6.0以上的版本,因此需要指定时区serverTimezone。
如果大家碰到了这个问题,在spring.datasource.druid中的url后面加上serverTimezone=UTC
(世界标准时间)就可以了
这样调整之后,运行结果就没有问题了。
既然能集成JdbcTemplate,那么肯定能集成Mybatis啦,我们再来创建一个 “小项目” 玩玩。
第一步:创建数据库
还是使用上面测试JdbcTemplate集成用到的数据库表(如下)
第二步:创建UserInfo和UserMapper
接着使用Mybatis逆向工程创建UserInfo,UserInfoMapper,UserInfo的代码就不放出来了哈,我这里为了能快速的演示出效果,就没有使用UserMapper.xml了,改用Mybatis的注解方式,也算是复习一下Mybatis的知识啦~
package com.marco.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.marco.domain.UserInfo;
public interface UserInfoMapper {
@Delete(value="delete from userinfo where u_id = #{value}")
int deleteByPrimaryKey(Integer uId);
@Insert("insert into userinfo(u_uname,u_pwd,u_tele,u_realname) values(#{uUname}, #{uPwd}, #{uTele}, #{uRealname})")
int insert(UserInfo userInfo);
@Update("update userinfo set u_uname=#{uUname}, u_pwd=#{uPwd}, u_tele=#{uTele}, u_realname=#{uRealname}")
int updateByPrimaryKey(UserInfo userInfo);
@Select("select u_id as uId, u_uname as uUname,u_pwd as uPwd,u_tele as uTele, u_realname as uRealname, u_regTime as uRegtime from userinfo where u_id = #{value}")
UserInfo selectByPrimaryKey(Integer uId);
@Select("select u_id as uId, u_uname as uUname,u_pwd as uPwd,u_tele as uTele, u_realname as uRealname, u_regTime as uRegtime from userinfo")
List<UserInfo> queryAllUser();
}
第三步:修改pom.xml
这里需要引入mybatis的启动装置mybatis-spring-boot-starter
另外为了测试分页,以及查看sql运行的日志,我这里还引进了spring-boot-starter-log4j
以及com.github.pagehelper
第四步:修改application.yml
#注入mybatis的配置文件路径
mybatis:
config-location: 'classpath:mybatis.cfg.xml'
第五步:创建mybatis.cfg.xml
然后创建mybatis.cfg.xml,设置log4j以及分页插件
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">plugin>
plugins>
configuration>
第六步:扫描UserInfoMapper
这一步虽然很简单,但是至关重要!如果没有扫描mapper,则会抛出下面的异常
package com.marco;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages={"com.marco.mapper"})
public class SpringbootDatasourceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDatasourceApplication.class, args);
}
}
不过还有一种解决问题的方式就是在UserInfoMapper上方添加@Mapper
注解,不过这种方式显得有些 “鸡肋” ,因为当我们的Mapper类多起来之后,每次都要在类上加这么个注解,这样操作会显得很繁琐,因此,比较推荐第一种方式。
第七步:测试
package com.marco;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.marco.domain.UserInfo;
import com.marco.mapper.UserInfoMapper;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootDatasourceApplicationTests {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
public void contextLoads() {
Page<Object> page = PageHelper.startPage(1, 5);
List<UserInfo> userInfos = userInfoMapper.queryAllUser();
for (UserInfo userInfo : userInfos) {
System.out.println(page.getTotal());
System.out.println(userInfo);
}
System.out.println(userInfoMapper.selectByPrimaryKey(1));
}
}
但是!这种结合mybatis.cfg.xml使用插件的方式显得特别的累赘和多余,因此我们会针对上面的步骤做一些简化,让整个结构看起来没有那么的 “臃肿”。
第一步:修改yml
删除config-location: 'classpath:mybatis.cfg.xml'
,然后添加以下配置
上面这两条配置分别帮我们完成了log日志的设置以及注入Mapper.xml的配置(如果用上面的方法需要在mybatis.cfg.xml中注入)
第二步:修改pom.xml
接着剔除咱们之前的mybatis.cfg.xml文件,加上pagehelper的 “启动装置”,并删除之前导入pagehelper的引入jar包
第三步:删除log4j.properties
接着删除咱们之前的log4j.properties,避免和我们上面注入的log4j的配置产生冲突