SpringBoot入门之三 数据访问及多数据源
1. springboot整合使用jdbcTemplate
1.1 pom文件引入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
1.2 yml
server :
port : 8081
spring:
datasource:
url: jdbc:mysql://localhost:3307/springboot?useUnicode=true&characterEncoding=utf8
###springboot2 多数据源有个bug url-》jdbc-url
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
1.3 Mapper代码
2. springboot整合使用mybatis
Mybatis是一款支持复杂的SQL语句,存储过程及高级映射的持久层的框架。使用Mybatis有两种方式,XML和注解。
2.1 SpringBoot+Mybatis注解版
Mybatis初期使用比较麻烦,需要很多配置文件、实体类、dao层映射、还有很多其他的配置。初期开发使用generator可以根据表结构自动生产实体类、dao层代码,这样是可以减轻一部分开发量;后期mybatis进行大量的优化,现在可以使用注解版本,自动管理dao层和配置文件。
mybatis-spring-boot-starte就是SpringBoot集成Mybatis的jar包,可以完全使用注解,无需配置文件,简单配置轻松上手。
2.1.1 在Maven pom.xml文件中加入Mybatis和MySQL的jar包
<!-- 整合mybatise需要的依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
2.1.2 配置application.yml文件
spring:
datasource:
url: jdbc:mysql://localhost:3307/springboot?useUnicode=true&characterEncoding=utf8
###springboot2 多数据源有个bug url-》jdbc-url
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
type : com.alibaba.druid.pool.DruidDataSource
springboot会自动加载spring.datasource.*相关配置,数据源就会自动注入到sqlSessionFactory中,sqlSessionFactory会自动注入到Mapper中。
mybatis-spring-boot-starter的2.0.0与2.1.1版本还是有区别,如果升级到2.1.1,同样的代码会报以下错误
Property ‘sqlSessionFactory’ or ‘sqlSessionTemplate’ are required
为解决多数据源问题,mybatis取消啦自动注入sqlSessionFactory等,可以手动注入,此处不详细展开。
在启动类中添加对mapper包扫描@MapperScan或者在每个Mapper类中增加上面添加注解@Mapper,推荐在启动类加注解,这样不用在每个Mapper类加注解。
@MapperScan("sample.springboot.teach3.mapper")
@SpringBootApplication
public class SpringbootTeach3Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootTeach3Application.class, args);
}
}
2.1.3 开发Mapper
所有的sql都在注解上。
@Select 是查询类的注解,所有的查询均使用这个
@Result 修饰返回的结果集,关联实体类属性和数据库字段一一对应,如果实体类属性和数据库属性名保持一致,就不需要这个属性来修饰。
@Insert 插入数据库使用,直接传入实体类会自动解析属性到对应的值
@Update 负责修改,也可以直接传入对象
@delete 负责删除
更多方法请参考mybatis官方地址
http://www.mybatis.org/mybatis-3/zh/java-api.html
2.1.4 使用
启动项目成功在浏览器输入本机的地址。
源码中controler层有完整的增删改查,这里就不贴了。
常见问题
注意#和$的问题?
#方式能够很大程度防止sql注入,#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”
方 式 无 法 防 止 S q l 注 入 , 方式无法防止Sql注入, 方式无法防止Sql注入,将传入的数据直接显示生成在sql中。如:order by u s e r i d user_id userid,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id。$方式一般用于传入数据库对象,例如传入表名。
使用MyBatis排序时使用order by 动态参数时需要注意,用$而不是#。
#{} 这种取值是编译好SQL语句再取值
${} 这种是取值以后再去编译SQL语句
2.2 SpringBoot+Mybatis XML版本
xml版本保持映射文件的老传统,优化主要体现在不需要实现dao的是实现层,而是在映射的xml文件中找到相应的sql语句。
2.2.1 配置文件application.yml文件中增加配置
#mybatis
mybatis:
mapper-locations: classpath:sample.springboot.teach3.mapper/*.xml
type-aliases-package: sample.springboot.teach3.model
2.2.2 Mapper XML本人使用mybatis generator生成器,具体配置生成器的方法不在此展开。代码如下
2.2.3Dao层
Dao层也是自动生成,可增加自定义接口
public interface UserMapper {
User findMyName(@Param("name") String name);
@Insert("INSERT INTO USERS(USER_NAME,AGE) VALUES(#{name},#{age})")
int add(@Param("name") String naem, @Param("age") Integer age);
int deleteByPrimaryKey(Integer id);
int insert(User record);
int insertSelective(User record);
User selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
}
2.2.4
启动,遇到错误如下
Cause: java.lang.IllegalArgumentException: Result Maps collection already contains value for xyx.dsw.dao.mapper.admin.quotationwish.TempTestTableMapper.TempTestTableResult
原因分析:
由于使用ibatis的TempTestTableMapper.xml实现接口TempTestTableMapper.java中的方法的时候的id有重复的值,查看XXXMapper.xml,发现执行多次mybatis generator 所致.
查询功能,在url页面的测试。
源码中controler层有完整的增删改查,这里就不贴了
注解和xml如何选择呢?
注解版适合简单快速的模式。
xml版比较适合大型项目,可以灵活的动态生成SQL,方便调整SQL。
3. 集成mybatis generator插件
方法
- eclipse下用maven插件+Mabatis-generator生成mybatis的文件,
- 安装MybatisGenerator插件 此方式不再此展开
3.1. 配置Maven pom.xml 文件
在pom.xml增加以下插件:
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version><!--jar包去生成对应类 需要连接数据库 数据连接的版本和项目中的一致 -->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
</dependencies>
<configuration><!--MBG配置文件的路径 -->
<configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
</configuration>
</plugin>
配置好Maven插件,下面需要配置插件需要mybatis-generator的配置文件
3.2. 配置mybatis-generator的配置文件
添加配置文件到resource下:
generatorConfig.xml的内容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id = "test " targetRuntime="MyBatis3">
<plugin type ="org.mybatis.generator.plugins.EqualsHashCodePlugin">
</plugin>
<plugin type ="org.mybatis.generator.plugins.SerializablePlugin">
</plugin>
<plugin type ="org.mybatis.generator.plugins.ToStringPlugin">
</plugin>
<commentGenerator><!-- 这个元素用来去除指定生成的注释中是否包含生成的日期 false:表示包含 --><!-- 如果生成日期,会造成即使修改一个字段,整个实体类所有属性都会发生变化,不利于版本控制,所以设置为true -->
<property name = "suppressDate" value="true" /><!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name = "suppressAllComments" value="true" />
</commentGenerator><!--数据库链接URL,用户名、密码 -->
<jdbcConnection driverClass ="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3307/springboot" userId="root" password="">
</jdbcConnection>
<javaTypeResolver>
<property name = "forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 生成实体类的包名和位置 -->
<javaModelGenerator targetPackage ="sample.springboot.teach3.model" targetProject="src/main/java">
<property name = "enableSubPackages" value="true" />
<property name = "trimStrings" value="true" />
</javaModelGenerator>
<!--生成映射文件的包名和位置 com/lu/mapper -->
<sqlMapGenerator targetPackage ="sample.springboot.teach3.mapper" targetProject="src/main/resources">
<property name = "enableSubPackages" value="true" />
</sqlMapGenerator><!-- 生成DAO的包名和位置 mybatis两种开发模式 xml 注解式 -->
<!-- <javaClientGenerator type = "XMLMAPPER" targetPackage="sample.springboot.teach3.mapper" targetProject="src/main/java">
<property name = "enableSubPackages" value="true" />
</javaClientGenerator> -->
<!-- 要生成哪些表 -->
<table tableName = "users" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
3.3 生成代码
在eclipse 中,选择pom.xml文件,击右键先择Run AS——>Maven Build… ——>在Goals框中输入:mybatis-generator:generate,
maven会先下载插件,然后生成代码。
看效果:
4. 配置多数据源
SpringBoot多数据源也就是在一个jar存在多个不同的jdbc数据库连接。
在一个项目中存在会员、订单、支付模块,只分包,并不算分布式项目。
分布式项目是将一个大的项目拆分多个不同子项目,子项目之间采用rpc远程调用通信技术。假设我们的数据库分为会员数据库、订单数据库、支付数据库等,存在不同的jdbc;多数据库如何定位自己的数据源:
- 分包名
sample.teach2.user-- 数据用就是会员,或用户
sample.teach2.order-订单数据库 - 注解形式
在service层引入@DataSource(“XXX”) ,不常用。本文仅使用第一种
4.1.首先定义数据源配置
#=====================multiple database config============================
spring:
datasource:
user:
jdbc-url: jdbc:mysql://localhost:3307/springboot?useUnicode=true&characterEncoding=utf8
###springboot2 多数据源有个bug url-》jdbc-url
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
order :
jdbc-url: jdbc:mysql://localhost:3307/springboot2?useUnicode=true&characterEncoding=utf8
###springboot2 多数据源有个bug url-》jdbc-url
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
### type : com.alibaba.druid.pool.DruidDataSource
4.2. 配置数据源的相关注入数据源对象
第一个数据源配置
@Configuration
@MapperScan(basePackages = "sample.springboot.teach3.user", sqlSessionTemplateRef = "userSqlSessionTemplate")
public class UserDataSourceConfig {
@Bean("userDataSource")
@ConfigurationProperties("spring.datasource.user")
public DataSource userDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "userSqlSessionFactory")
public SqlSessionFactory userSqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(userDataSource());
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "userTransactionManager")
public DataSourceTransactionManager userTrancationManager(@Qualifier("userDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "userSqlSessionTemplate")
public SqlSessionTemplate
userSqlSessionTemplate(@Qualifier("userSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
相关知识点:
1.使用@Bean可以创建一个bean对象交给spring容器管理
2.@Bean创建的bean对象的名称默认为方法名,也可以指定
3.@Bean方法参数表示,接收一个bean对象,默认按照type类型接收注入的对象,若要修改为byName方式,可以使用@Qualifier注解注入准确的对象
4.@Primary表示该bean为此类型的默认bean,在其他地方引用的时候用@Autowired即可按照类型注入,不受同类型多个对象影响
其他注解就不清楚了!
2.配置第二个数据源
@Configuration
@MapperScan(basePackages = "sample.springboot.teach3.order", sqlSessionTemplateRef = "orderSqlSessionTemplate")
public class OrderDataSourceConfig {
@Bean("orderDataSource")
@ConfigurationProperties("spring.datasource.order")
public DataSource orderDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "orderSqlSessionFactory")
public SqlSessionFactory orderSqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(orderDataSource());
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "orderTransactionManager")
public DataSourceTransactionManager orderTrancationManager(@Qualifier("orderDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "orderSqlSessionTemplate")
public SqlSessionTemplate
orderSqlSessionTemplate(@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
4.3. Dao数据持久层
public interface UserMapper {
User findMyName(@Param("name") String name);
@Insert("INSERT INTO USERS(USER_NAME,AGE) VALUES(#{name},#{age})")
int add(@Param("name") String name, @Param("age") Integer age);
int deleteByPrimaryKey(Integer id);
int insert(User record);
int insertSelective(User record);
User selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
}
public interface OrderMapper {
int deleteByPrimaryKey(Integer id);
int insert(Order record);
int insertSelective(Order record);
Order selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(Order record);
int updateByPrimaryKey(Order record);
}
上面两个接口分属两个数据源,
4.4 Service服务层,注意事务
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
public boolean addOrder(Integer userId, Integer total) {
Order order = new Order();
order.setTotal(Float.valueOf(total));
order.setUserId(userId);
int result = orderMapper.insertSelective(order);
return result > 0 ? true : false;
}
}
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public boolean addUser2(String userName, Integer age) {
int result = userMapper.add(userName, age);
return result > 0 ? true : false;
}
public User getById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
}
4.5 知识扩展
1.如果采用传统jpa方式,@EnableJpaRepositories无需配置,配置了也无影响。实现方式如下:
ds1相关DaoImpl
@PersistenceContext
private EntityManager entityManager;
ds2相关DaoImpl
@PersistenceContext(unitName = “secondDs”)
private EntityManager entityManager;
因为ds1的entityManger声明了@Primary,所以无需指明unitName,ds2必须指明。注入了准确的entityManager,就可以直接拿来操作数据库了。service层和上面一样的,@Transactional(“xxxManager”)指明事物管理器即可!
2.采用jdbcTemplate方式,直接注入到Service层对象即可,so easy!
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private TransactionTemplate transactionTemplate;
@Resource(name=“jdbcTemplate2”)
private JdbcTemplate jdbcTemplate2;
@Resource(name=“transactionTemplate2”)
private TransactionTemplate transactionTemplate2;
好了,spring boot 多数据源,完美解决! 而且三种数据库操作方法均支持,包括事物。已经经过实践证明了! 这是官方给出的最佳实践,只是官方文档没写细而已。
源码地址
https://gitee.com/xiaolaifeng/sample.springboot/tree/master/springboot-teach3
注解版:
XML版: