Spring Boot
2.2.0.M6 已发布,可以从里程碑仓库获取。

看完本文你将掌握如下知识点:

spring
在2.5版本以后开始支持注解的方式来配置依赖注入,可以用注解的方式来代替xml中的bean的描述,注解注入将会被容器在xml注入之前被处理,所以后者会被覆盖掉前者对于同一个属性的处理结果。

下一个 2.2 版本将是本月晚些时候的 RC1,GA 则会在 10 月中旬推出。

  1. Spring Boot对JDBC的支持
  2. Spring Boot项目多数据源的配置
  3. Spring Boot的事务管理
  4. Spring Boot项目多数据源的事务管理
  5. Spring Boot项目中使用Hibernate4的方法
  6. Spring Boot项目中使用Mybatis的方法

注解装配在spring 中默认是关闭的,所以需要在spring
的核心配置文件中配置一下才能使用,基于注解的装配模式,配置方式如下

此版本更新内容如下:

SpringBoot系列:Spring Boot学习笔记

<context:annotation-config />

新特性

  • 提供公共
    API,用于根据响应的状态代码确定请求的结果 #18150
  • 改进 WebMvcTags
    中非标准状态代码的处理 #17998
  • 使用任何 JavaMigration bean 自动配置
    Flyway #17993
  • 添加了 Issuer Validation
    的其他资源服务器配置 #17953
  • 默认配置
    ping(以前的应用程序)运行状况指示器 #17926
  • 删除默认的
    favicon #17925
  • 添加了对 dev-tools yaml
    配置的支持 #17915
  • 绕过远程 devtools 端点的 Spring Security
    身份验证 #17878
  • 在 Windows 上使用带有 cygwin 的 CLI
    时支持多个驱动器 #17872
  • 允许通过属性配置 Jetty
    的线程池 #17871
  • 使 AbstractErrorWebExceptionHandler 中的 logError()
    可重写 #17863
  • 在依赖关系管理中公开依赖管理插件版本 #17854
  • 将 confirm-type 属性添加到
    R​​abbitProperties #17848
  • 添加对配置 Spring Session JDBC
    刷新模式的支持 #17797
  • 添加 IBM DB2 JDBC
    驱动程序的依赖关系管理 #17782
  • 为 JMS
    侦听器容器的接收超时提供配置属性 #17332
  • 支持并行测试执行 #16179
  • 添加对健康指标组的支持 #14022

新版本还包含大量 bug
修复、文档更新和依赖升级,详情可查看更新列表。

(文/开源中国)    

Spring
Boot针对企业开发场景提供了各种『开箱即用』的spring-boot-starter-xxx自动配置依赖模块,这就使得我们开发Spring应用更加快速和高效。比如我们前面创建web项目时使用到的spring-boot-starter-web

常用的注解

这些spring-boot-starter-xxx不但包含了对该功能的全部依赖包,同时也提供了该功能的自动配置类。我们本节要讨论的『数据访问』就是基于这些spring-boot-starter-xxx的自动配置依赖模块。

@Required:该注解应用于设值的方法

jdk版本:java version “1.8.0_31”数据库:10.1.16-MariaDB脚本

@Autowired:该注解应用于值设值方法,非设值方法,构造方法和变量

# 创建库1CREATE SCHEMA `springboot1` DEFAULT CHARACTER SET utf8 ;CREATE TABLE `springboot1`.`person` ( `p_id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `p_name` VARCHAR NULL COMMENT '姓名', `p_age` INT NULL COMMENT '年龄', PRIMARY KEY ENGINE = InnoDBCOMMENT = '人员信息表';INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('1', '张三', '20');INSERT INTO `springboot1`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('2', '李四', '25');# 创建库2CREATE SCHEMA `springboot2` DEFAULT CHARACTER SET utf8 ;CREATE TABLE `springboot2`.`person` ( `p_id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `p_name` VARCHAR NULL COMMENT '姓名', `p_age` INT NULL COMMENT '年龄', PRIMARY KEY ENGINE = InnoDBCOMMENT = '人员信息表';INSERT INTO `springboot2`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('1', '张三', '20');INSERT INTO `springboot2`.`person` (`p_id`, `p_name`, `p_age`) VALUES ('2', '李四', '25');

@Qualifer:该注解和@Autowired搭配使用,用于消除特定bean自动装配的歧义

创建项目

新建一个springboot项目,依赖选择web和jdbc

图片 1

项目创建成功后查看pom,会看到添加了spring-boot-starter-jdbc的依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency>

简单介绍一下Spring bean 的生命周期

配置项目

在pom中增加MySQL依赖

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version></dependency>

application.properties中添加数据源配置信息

#datasourcespring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/springboot1?useUnicode=true&characterEncoding=utf-8spring.datasource.username=rootspring.datasource.password=newpwd

bean 定义:有两种方式初始化。

项目代码

本例只做简单演示,所以只创建如下3个类,并用一个单元测试类进行测试Model:Person

public class Person implements Serializable { private static final long serialVersionUID = -1L; private Long id; private String name; private Integer age; //getter and setter @Override public String toString() { return "Person{" + ", name='" + name + ''' + ", age=" + age + '}'; }}

Dao:PersonDao

@Repositorypublic class PersonDao { @Autowired private JdbcTemplate jdbcTemplate; public int savePerson(Person person){ String sql = "INSERT INTO `springboot1`.`person` (`p_name`, `p_age`) VALUES "; int result = jdbcTemplate.update(sql,new Object[]{person.getName(),person.getAge; return result; } public List<Person> getAllPersonList(){ String sql = "select * from person s"; List<Person> list = jdbcTemplate.query(sql,new PersonMapper; return list; } class PersonMapper implements RowMapper<Person>{ @Override public Person mapRow(ResultSet resultSet, int i) throws SQLException { Person person = new Person(); person.setId(resultSet.getLong; person.setName(resultSet.getString); person.setAge(resultSet.getInt; return person; } }}

Service:PersonService

@Servicepublic class PersonService { @Autowired private PersonDao personDao; public int savePserson(Person person){ return personDao.savePerson; } public List<Person> getAllPersonList(){ return personDao.getAllPersonList(); }}

单元测试:SpringbootjdbcdemoApplicationTests

@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringbootjdbcdemoApplicationTests { @Autowired private PersonService personService; @Test public void savePerson(){ Person person = new Person(); person.setName; person.setAge; int result = personService.savePserson; Assert.assertEquals; } @Test public void getAllPersonList(){ List<Person> list = personService.getAllPersonList(); System.out.println(list.size; for(Person person : list){ System.out.println; } }}

说明实际上,项目加入spring-boot-starter-jdbc的依赖后,即可在项目代码中通过@Autowired自动注入JdbcTemplate。而数据源的配置则在application.properties中进行配置。

如果不想使用spring-boot-starter-jdbc带来的默认依赖和自动配置,那么采用如下的方式,效果是一样的。

1.在配置文件中通过指定init-method属性来完成

使用自定义的DataSourceConfig

修改pom中的依赖,去掉对spring-boot-starter-jdbc的依赖,并加入对spring-jdbc的依赖,这样我们就失去了对JDBC的自动配置功能了。

 <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.4.RELEASE</version> </dependency>

启动类中去掉对DataSourceAutoConfiguration的自动配置支持

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})public class SpringbootjdbcdemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootjdbcdemoApplication.class, args); }}

创建DataSourceConfig配置类

@Configurationpublic class DataSourceConfig { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Bean(name = "dataSource") public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl; dataSource.setUsername; dataSource.setPassword; return dataSource; } @Bean(name = "jdbcTemplate") public JdbcTemplate jdbcTemplate(){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource; return jdbcTemplate; }}

其它代码不需要任何修改,运行效果一致。

说明为什么SpringBoot为我们提供了spring-boot-starter-jdbc的自动配置解决方案,我们还要自己配置呢,这是因为自动配置并不是那么的强大,spring-boot-starter-jdbc只能支持单一的数据源配置,如果项目中需要关联多个数据源,就需要我们自己处理了。

比如我们在环境准备中创建了两个数据库,接下来我们在项目中增加多数据源的配置。

application.properties中添加数据源配置信息

#datasource2spring.datasource.driver-class-name2=com.mysql.jdbc.Driverspring.datasource.url2=jdbc:mysql://localhost:3306/springboot2?useUnicode=true&characterEncoding=utf-8spring.datasource.username2=rootspring.datasource.password2=newpwd

然后在DataSourceConfig配置类中增加如下内容:

@Value("${spring.datasource.driver-class-name2}")String driverClass2;@Value("${spring.datasource.url2}")String url2;@Value("${spring.datasource.username2}")String userName2;@Value("${spring.datasource.password2}")String passWord2;@Bean(name = "dataSource2")public DataSource dataSource2() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClass2); dataSource.setUrl; dataSource.setUsername(userName2); dataSource.setPassword(passWord2); return dataSource;}@Bean(name = "jdbcTemplate2")public JdbcTemplate jdbcTemplate2(){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource2; return jdbcTemplate;}

此时需要在Dao中将@Autowired注解替换成@Resource(name = "jdbcTemplate"),来明确指定要使用哪一个jdbcTemplate对象。

说明关于如何在项目中使用Hibernate4框架,可以参考:SpringMVC4零配置

2.实现org.springframwork.beans.factory.InitiazingBean 接口

JDBC事务管理

如果我们项目中使用的是JDBC的数据访问方案,并且容器中只注册了一个DataSource,那么SpringBoot就会为我们开启DataSourceTransactionManagerAutoConfiguration的自动配置类,其会在容器中注册一个DataSourceTransactionManager事务管理器,同时会开启对注解式事务@Transactional的支持。感兴趣的可以看一下DataSourceTransactionManagerAutoConfiguration的源码。

@Transactional是Spring框架提供的,配置方法参考下面的代码

//一般我们会在业务实现类上声明事务注解//当前表示需要在事务中运行,可以执行更新和删除操作,遇到异常则回滚@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = { Exception.class })public class PersonService{ //方法上也可以标注事务注解,方法上注解声明会覆盖类上的 //一般查询操作readOnly设置为true,增删该操作设置为false @Transactional(readOnly = true) public List<Person> getAllPersonList(){ //do something } //不加@Transactiona注解,则使用类上的设置 public int savePserson(Person person){ //do something }}

如果在测试类上声明@Transactional,则会开启自动回滚,不会产生脏数据

@RunWith(SpringRunner.class)@SpringBootTest@Transactionalpublic class SpringbootjdbcdemoApplicationTests {…………}

如果希望自己配置事务,可以在配置类中增加事务管理器的配置,比如,我们在DataSourceConfig中增加如下配置:

@Configuration//启用注解事务管理,使用CGLib代理@EnableTransactionManagement(proxyTargetClass = true)public class DataSourceConfig { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Bean(name = "dataSource") public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl; dataSource.setUsername; dataSource.setPassword; return dataSource; } @Bean(name = "jdbcTemplate") public JdbcTemplate jdbcTemplate(){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource; return jdbcTemplate; } @Bean public DataSourceTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource; }}

说明上面的方法只是针对单一数据源进行事务管理的,但是项目中经常会用到多数据源的情况,那么要如何进行事务管理呢?

我们上文讲到了可以在项目中通过配置类,自己配置多个数据源,并通过DataSourceConfig进行了演示,接下来我们添加多个事务管理器。

@Configuration//启用注解事务管理,使用CGLib代理@EnableTransactionManagement(proxyTargetClass = true)public class DataSourceConfig { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Value("${spring.datasource.driver-class-name2}") String driverClass2; @Value("${spring.datasource.url2}") String url2; @Value("${spring.datasource.username2}") String userName2; @Value("${spring.datasource.password2}") String passWord2; @Bean(name = "dataSource") public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl; dataSource.setUsername; dataSource.setPassword; return dataSource; } @Bean(name = "jdbcTemplate") public JdbcTemplate jdbcTemplate(){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource; return jdbcTemplate; } @Bean(name = "transactionManager") public DataSourceTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource; } @Bean(name = "dataSource2") public DataSource dataSource2() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClass2); dataSource.setUrl; dataSource.setUsername(userName2); dataSource.setPassword(passWord2); System.out.println; return dataSource; } @Bean(name = "jdbcTemplate2") public JdbcTemplate jdbcTemplate2(){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource2; return jdbcTemplate; } @Bean(name = "transactionManager2") public DataSourceTransactionManager transactionManager2() { return new DataSourceTransactionManager(dataSource2; }}

这时,我们必须在@Transactional注解中指定要使用哪一个事务管理器

@Service@Transactional(transactionManager = "transactionManager",propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = { Exception.class })public class PersonService { @Autowired private PersonDao personDao; public int savePserson(Person person){ return personDao.savePerson; } @Transactional(transactionManager = "transactionManager",readOnly = true) public List<Person> getAllPersonList(){ return personDao.getAllPersonList(); } @Transactional(transactionManager = "transactionManager2",propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = { Exception.class }) public int savePserson2(Person person){ return personDao.savePerson2; } @Transactional(transactionManager = "transactionManager2",readOnly = true) public List<Person> getAllPersonList2(){ return personDao.getAllPersonList2(); }}

说明这样做并不美好,不能对多个数据源同时进行事务管理,比如,我们在一个业务方法里同时对两个数据源进行操作,我们希望只要有一个发生异常,则两个数据源的数据都进行回滚。

那要怎么做呢,我们接着往下看。

bean调用:有三种方法可以得到bean实例并运行调用

多数据源事务管理

这里推荐使用Atomikos,Atomikos支持Mysql、Oracle等多种数据库,可与多种ORM框架集成,如MyBatis、JPA、Hibernate等等,同时支持各种容器下JNDI的多数据源管理。Atomikos官网提供了各种情况下使用Atomikos的Example,本文只对使用JDBC时的情况进行说明。

目前maven中央仓库的最新版本是4.0.4,使用Atomikos,需要在项目中加入如下依赖:

<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>4.0.4</version></dependency><dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>4.0.4</version></dependency><dependency> <groupId>com.atomikos</groupId> <artifactId>transactions</artifactId> <version>4.0.4</version></dependency><dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-util</artifactId> <version>4.0.4</version></dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version></dependency>

DataSourceConfig进行改造:

package com.example;import com.atomikos.icatch.jta.UserTransactionImp;import com.atomikos.icatch.jta.UserTransactionManager;import com.atomikos.jdbc.AtomikosDataSourceBean;import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.annotation.EnableTransactionManagement;import org.springframework.transaction.jta.JtaTransactionManager;import javax.sql.DataSource;import javax.transaction.TransactionManager;import javax.transaction.UserTransaction;@Configuration//启用注解事务管理,使用CGLib代理@EnableTransactionManagement(proxyTargetClass = true)public class DataSourceConfig { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Value("${spring.datasource.driver-class-name2}") String driverClass2; @Value("${spring.datasource.url2}") String url2; @Value("${spring.datasource.username2}") String userName2; @Value("${spring.datasource.password2}") String passWord2; @Bean(name = "userTransaction") public UserTransaction userTransaction() throws Throwable { UserTransactionImp userTransactionImp = new UserTransactionImp(); userTransactionImp.setTransactionTimeout; return userTransactionImp; } @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") public TransactionManager atomikosTransactionManager() throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager(); userTransactionManager.setForceShutdown; return userTransactionManager; } @Bean(name = "transactionManager") @DependsOn({ "userTransaction", "atomikosTransactionManager" }) public PlatformTransactionManager transactionManager() throws Throwable { UserTransaction userTransaction = userTransaction(); TransactionManager atomikosTransactionManager = atomikosTransactionManager(); JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, atomikosTransactionManager); jtaTransactionManager.setAllowCustomIsolationLevels; return jtaTransactionManager; } @Bean(name = "dataSource", initMethod = "init", destroyMethod = "close") public DataSource dataSource() { System.out.println("dataSource init"); //Oracle:oracle.jdbc.xa.client.OracleXADataSource //Druid:com.alibaba.druid.pool.xa.DruidXADataSource //Postgresql:org.postgresql.xa.PGXADataSource MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl; mysqlXaDataSource.setPassword; mysqlXaDataSource.setUser; mysqlXaDataSource.setPinGlobalTxToPhysicalConnection; AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("dataSource"); xaDataSource.setMinPoolSize; xaDataSource.setPoolSize; xaDataSource.setMaxPoolSize; xaDataSource.setBorrowConnectionTimeout; xaDataSource.setReapTimeout; xaDataSource.setMaxIdleTime; xaDataSource.setMaintenanceInterval; return xaDataSource; } @Bean(name = "dataSource2", initMethod = "init", destroyMethod = "close") public DataSource dataSource2() { System.out.println("dataSource2 init"); MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl; mysqlXaDataSource.setPassword(passWord2); mysqlXaDataSource.setUser(userName2); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection; AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("dataSource2"); xaDataSource.setMinPoolSize; xaDataSource.setPoolSize; xaDataSource.setMaxPoolSize; xaDataSource.setBorrowConnectionTimeout; xaDataSource.setReapTimeout; xaDataSource.setMaxIdleTime; xaDataSource.setMaintenanceInterval; return xaDataSource; } @Bean(name = "jdbcTemplate") public JdbcTemplate jdbcTemplate(){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource; return jdbcTemplate; } @Bean(name = "jdbcTemplate2") public JdbcTemplate jdbcTemplate2(){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource2; return jdbcTemplate; }}

项目编译路径下可以创建一个jta.properties文件,用于对Atomikos的相关属性进行配置,不过也可以不加这个文件,因为所有的属性都有默认值。

com.atomikos.icatch.enable_logging=truecom.atomikos.icatch.force_shutdown_on_vm_exit=falsecom.atomikos.icatch.automatic_resource_registration=truecom.atomikos.icatch.checkpoint_interval=500com.atomikos.icatch.serial_jta_transactions=truecom.atomikos.icatch.default_jta_timeout=10000com.atomikos.icatch.max_timeout=300000com.atomikos.icatch.log_base_dir=./com.atomikos.icatch.threaded_2pc=falsecom.atomikos.icatch.max_actives=50com.atomikos.icatch.log_base_name=tmlogjava.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactorycom.atomikos.icatch.client_demarcation=falsejava.naming.provider.url=rmi://localhost:1099com.atomikos.icatch.rmi_export_class=nonecom.atomikos.icatch.trust_client_tm=falsecom.atomikos.icatch.forget_orphaned_log_entries_delay=86400000com.atomikos.icatch.recovery_delay=${com.atomikos.icatch.default_jta_timeout}com.atomikos.icatch.oltp_max_retries=5com.atomikos.icatch.oltp_retry_interval=10000com.atomikos.icatch.allow_subtransactions=true

Atomikos与Hibernate4集成方法与JDBC类似,我们在pom中加入hibernate的依赖,并对DataSourceConfig进行改造pom

<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.3.5.Final</version></dependency>

DataSourceConfig

package com.example;import com.atomikos.icatch.jta.UserTransactionImp;import com.atomikos.icatch.jta.UserTransactionManager;import com.atomikos.jdbc.AtomikosDataSourceBean;import com.example.hibernate.CP_HibernateDAO;import com.example.hibernate.impl.CP_Hibernate4DAOImpl;import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.orm.hibernate4.LocalSessionFactoryBean;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.annotation.EnableTransactionManagement;import org.springframework.transaction.jta.JtaTransactionManager;import javax.sql.DataSource;import javax.transaction.TransactionManager;import javax.transaction.UserTransaction;import java.util.Properties;@Configuration@EnableTransactionManagement(proxyTargetClass = true)public class DataSourceConfig { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Value("${spring.datasource.driver-class-name2}") String driverClass2; @Value("${spring.datasource.url2}") String url2; @Value("${spring.datasource.username2}") String userName2; @Value("${spring.datasource.password2}") String passWord2; @Bean(name = "userTransaction") public UserTransaction userTransaction() throws Throwable { UserTransactionImp userTransactionImp = new UserTransactionImp(); userTransactionImp.setTransactionTimeout; return userTransactionImp; } @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") public TransactionManager atomikosTransactionManager() throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager(); userTransactionManager.setForceShutdown; return userTransactionManager; } @Bean(name = "transactionManager") @DependsOn({ "userTransaction", "atomikosTransactionManager" }) public PlatformTransactionManager transactionManager() throws Throwable { System.out.println(); UserTransaction userTransaction = userTransaction(); TransactionManager atomikosTransactionManager = atomikosTransactionManager(); JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, atomikosTransactionManager); jtaTransactionManager.setAllowCustomIsolationLevels; return jtaTransactionManager; } @Bean(name = "dataSource", initMethod = "init", destroyMethod = "close") public DataSource dataSource() { System.out.println(); MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl; mysqlXaDataSource.setPassword; mysqlXaDataSource.setUser; mysqlXaDataSource.setPinGlobalTxToPhysicalConnection; AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("dataSource"); xaDataSource.setMinPoolSize; xaDataSource.setPoolSize; xaDataSource.setMaxPoolSize; xaDataSource.setBorrowConnectionTimeout; xaDataSource.setReapTimeout; xaDataSource.setMaxIdleTime; xaDataSource.setMaintenanceInterval; return xaDataSource; } @Bean(name = "dataSource2", initMethod = "init", destroyMethod = "close") public DataSource dataSource2() { System.out.println(); MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl; mysqlXaDataSource.setPassword(passWord2); mysqlXaDataSource.setUser(userName2); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection; AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("dataSource2"); xaDataSource.setMinPoolSize; xaDataSource.setPoolSize; xaDataSource.setMaxPoolSize; xaDataSource.setBorrowConnectionTimeout; xaDataSource.setReapTimeout; xaDataSource.setMaxIdleTime; xaDataSource.setMaintenanceInterval; return xaDataSource; } @Bean(name = "sessionFactory") public LocalSessionFactoryBean localSessionFactoryBean() { System.out.println("sessionFactory"); LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); sessionFactory.setDataSource(dataSource; //扫描实体对象的目录,不同的数据源,实体要存放不同的目录 String[] packagesToScan = new String[] { "com.example.model.ds1" }; sessionFactory.setPackagesToScan(packagesToScan); Properties hibernateProperties = new Properties(); hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect"); hibernateProperties.setProperty("hibernate.show_sql", "true"); //开启Hibernate对JTA的支持 hibernateProperties.setProperty("hibernate.current_session_context_class", "jta"); hibernateProperties.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory"); sessionFactory.setHibernateProperties(hibernateProperties); return sessionFactory; } @Bean(name = "sessionFactory2") public LocalSessionFactoryBean localSessionFactoryBean2() { System.out.println("sessionFactory2"); LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean(); sessionFactory.setDataSource(dataSource2; //扫描实体对象的目录,不同的数据源,实体要存放不同的目录 String[] packagesToScan = new String[] { "com.example.model.ds2" }; sessionFactory.setPackagesToScan(packagesToScan); Properties hibernateProperties = new Properties(); hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect"); hibernateProperties.setProperty("hibernate.show_sql", "true"); //开启Hibernate对JTA的支持 hibernateProperties.setProperty("hibernate.current_session_context_class", "jta"); hibernateProperties.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory"); sessionFactory.setHibernateProperties(hibernateProperties); return sessionFactory; } @Bean(name = "hibernateDAO") public CP_HibernateDAO hibernate4Dao() { System.out.println("hibernateDAO"); CP_Hibernate4DAOImpl dao = new CP_Hibernate4DAOImpl(); //绑定SessionFactory dao.setSessionFactory(localSessionFactoryBean().getObject; return dao; } @Bean(name = "hibernateDAO2") public CP_HibernateDAO hibernate4Dao2() { System.out.println("hibernateDAO2"); CP_Hibernate4DAOImpl dao = new CP_Hibernate4DAOImpl(); //绑定SessionFactory2 dao.setSessionFactory(localSessionFactoryBean2().getObject; return dao; }}

@Entity@Table(name = "person")public class Person implements Serializable { private static final long serialVersionUID = -1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "p_id") private Long id; @Column(name = "p_name") private String name; @Column(name = "p_age") private Integer age; //setter and getter}

CP_HibernateDAO是我们自定义的Hibernate的通用Dao接口,其定义的方法和和实现类CP_Hibernate4DAOImpl代码如下:

package com.example.hibernate;import java.util.List;public interface CP_HibernateDAO { public List<?> findAll(Class<?> entityClazz, String... str); public void save(Object entity);}

package com.example.hibernate.impl;import com.example.hibernate.CP_HibernateDAO;import org.hibernate.Criteria;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.criterion.DetachedCriteria;import java.util.List;public class CP_Hibernate4DAOImpl implements CP_HibernateDAO { private SessionFactory sessionFactory; public SessionFactory getSessionFactory() { return sessionFactory; } //绑定SessionFactory public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } private Session getHibernateSession() { Session session = sessionFactory.openSession(); return session; } /* * @see com.example.hibernate.CP_HibernateDAO#findAll() */ @Override public List<?> findAll(Class<?> entityClazz, String... str) { DetachedCriteria dc = DetachedCriteria.forClass(entityClazz); List<?> list = findAllByCriteria; return list; } /* * @see com.example.hibernate.CP_HibernateDAO#save(java.lang.Object) */ @Override public void save(Object entity) { getHibernateSession().save; //注意这里一定要执行flush方法 getHibernateSession; } public List<?> findAllByCriteria(DetachedCriteria detachedCriteria) { // TODO Auto-generated method stub Criteria criteria = detachedCriteria .getExecutableCriteria(getHibernateSession; return criteria.list(); }}

说明需要注意两点:

  1. session必须使用sessionFactory.openSession()的方式获得,不能使用sessionFactory.getCurrentSession()。
  2. 更新操作必须调用session.flush()方法。

Spring配置文件的方式,可以参考:Spring4+Hibernate4+Atomikos3.3多数据源事务管理

创建项目时,我们可以选择mybatis-spring-boot-starter依赖,这样可以激活SpringBoot对Mybatis的自动配置类。

pom中添加依赖

<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version></dependency>

application.properties中添加mybaits的自动配置属性,可以查看MybatisProperties了解可以配置哪些属性

#mapper配置文件路径,如果是基于注解的形式可以不需要配置该属性mybatis.mapper-locations=classpath:mapper/*.xml

Mapper接口上要配置@Mapper注解,因为mybatis-spring-boot-starter的自动配置会扫描@Mapper注解来注册Mapper接口。

@Mapperpublic interface PersonMapper { //………………}

此时同样可以使用@Transactional注解

说明可以使用maven的mybatis-generator插件自动生成代码,参考maven插件–MyBatis自动生成代码

mybatis-spring-boot-starter不利于扩展,所以还是我们自己实现个mybitas的配置类吧。

pom中去掉mybatis-spring-boot-starter的依赖,增加mybatis的依赖

 <!-- <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency>--> <!--Mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.4.RELEASE</version> </dependency>

创建MyBatisConfig

@Configuration@EnableTransactionManagement(proxyTargetClass = true)public class MyBatisConfig { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Bean(name = "dataSource") public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl; dataSource.setUsername; dataSource.setPassword; return dataSource; } @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactoryBean() { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource; //添加XML目录 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml")); return bean.getObject(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException; } } @Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } @Bean public PlatformTransactionManager annotationDrivenTransactionManager() { return new DataSourceTransactionManager(dataSource; }}

MyBatisMapperScannerConfig,基于包扫描Mapper,此时不需要配置@Mapper注解

@Configuration//必须在MyBatisConfig注册后再加载MapperScannerConfigurer,否则会报错@AutoConfigureAfter(MyBatisConfig.class)public class MyBatisMapperScannerConfig { @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); mapperScannerConfigurer.setBasePackage("com.example.mapper"); return mapperScannerConfigurer; }}

关闭DataSourceAutoConfiguration,因为这里我们配置了数据源,所以需要关闭该自动配置,另外,MybatisAutoConfiguration也是基于DataSourceAutoConfiguration的,所以关闭了DataSourceAutoConfiguration也就同时关闭了MybatisAutoConfiguration

pom

 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.4.RELEASE</version> </dependency> <!--Mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-util</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version> </dependency>

MyBatisConfig

@Configuration@EnableTransactionManagement(proxyTargetClass = true)public class MyBatisConfig { @Value("${spring.datasource.driver-class-name}") String driverClass; @Value("${spring.datasource.url}") String url; @Value("${spring.datasource.username}") String userName; @Value("${spring.datasource.password}") String passWord; @Value("${spring.datasource.driver-class-name2}") String driverClass2; @Value("${spring.datasource.url2}") String url2; @Value("${spring.datasource.username2}") String userName2; @Value("${spring.datasource.password2}") String passWord2; @Bean(name = "userTransaction") public UserTransaction userTransaction() throws Throwable { UserTransactionImp userTransactionImp = new UserTransactionImp(); userTransactionImp.setTransactionTimeout; return userTransactionImp; } @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") public TransactionManager atomikosTransactionManager() throws Throwable { UserTransactionManager userTransactionManager = new UserTransactionManager(); userTransactionManager.setForceShutdown; return userTransactionManager; } @Bean(name = "transactionManager") @DependsOn({ "userTransaction", "atomikosTransactionManager" }) public PlatformTransactionManager transactionManager() throws Throwable { UserTransaction userTransaction = userTransaction(); TransactionManager atomikosTransactionManager = atomikosTransactionManager(); JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, atomikosTransactionManager); jtaTransactionManager.setAllowCustomIsolationLevels; return jtaTransactionManager; } @Bean(name = "dataSource", initMethod = "init", destroyMethod = "close") public DataSource dataSource() { System.out.println("dataSource init"); MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl; mysqlXaDataSource.setPassword; mysqlXaDataSource.setUser; mysqlXaDataSource.setPinGlobalTxToPhysicalConnection; AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("dataSource"); xaDataSource.setMinPoolSize; xaDataSource.setPoolSize; xaDataSource.setMaxPoolSize; xaDataSource.setBorrowConnectionTimeout; xaDataSource.setReapTimeout; xaDataSource.setMaxIdleTime; xaDataSource.setMaintenanceInterval; return xaDataSource; } @Bean(name = "dataSource2", initMethod = "init", destroyMethod = "close") public DataSource dataSource2() { System.out.println("dataSource2 init"); MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl; mysqlXaDataSource.setPassword(passWord2); mysqlXaDataSource.setUser(userName2); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection; AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("dataSource2"); xaDataSource.setMinPoolSize; xaDataSource.setPoolSize; xaDataSource.setMaxPoolSize; xaDataSource.setBorrowConnectionTimeout; xaDataSource.setReapTimeout; xaDataSource.setMaxIdleTime; xaDataSource.setMaintenanceInterval; return xaDataSource; } //基于xml式Mapper @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactoryBean() { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource; //添加Mapper配置文件的目录 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { bean.setMapperLocations(resolver.getResources("classpath:mapper/ds1/*.xml")); return bean.getObject(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException; } } @Bean(name = "sqlSessionTemplate") public SqlSessionTemplate sqlSessionTemplate() { return new SqlSessionTemplate(sqlSessionFactoryBean; } //基于注解式Mapper @Bean(name = "sqlSessionFactory2") public SqlSessionFactory sqlSessionFactoryBean2() { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource2; try { return bean.getObject(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException; } } @Bean(name = "sqlSessionTemplate2") public SqlSessionTemplate sqlSessionTemplate2() { return new SqlSessionTemplate(sqlSessionFactoryBean2; }}

MyBatisMapperScannerConfig

@Configuration//必须在MyBatisConfig注册后再加载MapperScannerConfigurer,否则会报错@AutoConfigureAfter(MyBatisConfig.class)public class MyBatisMapperScannerConfig { @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); //绑定datasorce的sqlSessionFactory mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); //扫描ds1目录来注册Mapper接口 mapperScannerConfigurer.setBasePackage("com.example.mapper.ds1"); return mapperScannerConfigurer; } @Bean public MapperScannerConfigurer mapperScannerConfigurer2() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); //绑定datasorce2的sqlSessionFactory mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory2"); //扫描ds2目录来注册Mapper接口 mapperScannerConfigurer.setBasePackage("com.example.mapper.ds2"); return mapperScannerConfigurer; }}

这里要说明的是,如果两个数据源下的Mapper起了相同的类名,虽然他们在不同的包路径下,启动也会报错了,因为默认注册Mapper时使用的是类名称,此时可以在Mapper上加上@Component(“personMapper”)注解

Spring
Boot为我们提供了大量的spring-boot-starter-xxx来加快我们的开发流程,创建项目时就可以看到可供选择的各种spring-boot-starter-xxx,那么这么多的spring-boot-starter-xxx,我们是否都需要了解呢,如果项目中需要用到某一个功能,是否就应该加入这个spring-boot-starter-xxx呢?

笔者人为,spring-boot-starter-xxx提供的完整jar包依赖和自动配置固然很好,但是当我们要在项目中加入某一个功能时,作为开发人员,是应该清楚的知道该功能的依赖关系和配置逻辑的,所以并不一定需要引入SpringBoot的spring-boot-starter-xxx,而且SpringBoot对这些spring-boot-starter-xxx做的自动配置,如果我们并不熟悉和十分清楚,往往会给我们开发人员造成不明所以的困扰,所以,笔者建议,在对SpringBoot提供的某一个spring-boot-starter-xxx所提供的功能并不十分清楚时,还是使用配置类的方式吧。

还有,由于某些自动配置类的激活是根据项目中是否包含某个class或容器中是否注册了某个bean,所以笔者建议,如果项目中引入了新的jar包,或者手工注册了某个bean,都要通过debug的方式查看是否开启了某个自动配置。

另外,本文代码只是为了辅助说明,比如DriverManagerDataSource正式环境不建议使用,请更换为其它数据源,比如BasicDataSource

本文示例代码下载地址:

bean销毁:销毁的两种方式

1.使用配置文件指定的destory-method 属性

2.实现org.springframwork.bean.factory.DisposeableBean接口

Spring 能帮助我们做什么?

a.Spring能帮助我们根据配置文件创建及组装对象之间的依赖关系。

Spring
根据配置文件来进行创建及组装对象之间的依赖关系,只需要改配置文件即可

b.Spring面向切面编程能帮助我们无耦合的实现日志记录。性能统计,安全控制。

Spring
面向切面编程能提供一种更好的方式来完成,一般通过配置方式,而且不需要,在现在代码中添加任何额外代码

,现有代码专注业务逻辑

c.Spring还能和第三方数据库访问框架(如:Hibeanate,JPA)无缝集成。而且自己也提供了一套JDBC访问模板,来方便访问数据库

d,Spring能非常简单的帮助我们管理数据库事务

采用Spring我们只需要连接,执行SQL。其他事务相关的都交给Spring来管理了

e.Spring 还能与第三方Web(如Struts,JSF)
框架无缝集成,而且也自己提供了一套Spring MVC框架,来方便web层搭建

f.Spring能方便的与JavaEE(如:Java Mail
任务调度)整合,与更多技术整合,。

BeanFactory常用的实现类有哪些? Bean
工厂是工厂模式的一个实现,提供控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离,常用的beanFatcory实现所

有DefaultListableBeacFactory。XmlBeanFactory
.ApplicationContext等,XMLBeanFactory最常用的就是,org.springframework.beans.factory.xml.XmlBeanFactory它根据XML文件中的定义加载beans
该容器从xml文件中读取配置元数据并用他去创建一个完全配置的系统或应用