springboot+atomikos+多数据源管理事务(mysql 8.0)

  • Post author:
  • Post category:mysql

引用 https://www.cnblogs.com/coloz/p/10845058.html

jta:Java Transaction API,即是java中对事务处理的api 即 api即是接口的意思

atomikos:Atomikos TransactionsEssentials 是一个为Java平台提供增值服务的并且开源类事务管理器

 

目录

1.结构

2.pom依赖

3.创建本地数据库+表

4.application.yml 

5.实体类

6.mapper接口

7.service

8.Controller

9.配置数据源(*******重点总是在最后********)

10.测试

备注说明:

可能存在的问题:

1、using the Connector/J connection property ‘autoReconnect=true’ to avoid this problem.    https://blog.csdn.net/zljjava/article/details/7996091

日志:using the Connector/J connection property ‘autoReconnect=true’ to avoid this problem.  

MySQL数据库连接超时(wait_timeout)问题的处理

2、Atomikos数据连接池源码,弄清testQuery

 

 

1.结构

 

 

2.pom依赖

我这里使用本地数据库是mysql8,   

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!--<version>2.0.0.RELEASE</version>-->
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <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>
        <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version><!-- 1.3.0以上的版本没有@MapperScan以及@Select注解 -->
        </dependency>
        <!-- automatic+jta的分布式事务管理 -->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jta-atomikos -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--boot 2.1默认 mysql8的版本; boot 2.0默认mysql5版本-->
            <version>8.0.13</version>
            <!--<version>5.1.46</version>-->
            <!--<scope>runtime</scope>-->
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

3.创建本地数据库+表

 

4.application.yml 

server:
  port: 8080
  servlet:
  #    # 项目contextPath
    context-path: /manyDatasource

spring:
  application:
      name: manyDatasource
  datasource:
#  spring.datasource.test1
#    druid:
      test1:
#      jdbc-url,url,jdbcurl哪个合适用哪个
        jdbcurl: jdbc:mysql://localhost:3306/test1?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        initial-size: 1
        min-idle: 1
        max-active: 20
        test-on-borrow: true
#        driver-class-name: com.mysql.jdbc.Driver
#        下面是最新的mysql8版本推荐的驱动
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
#        下面是另外加的配置数据源的参数
        minPoolSize: 3
        maxPoolSize: 25
        maxLifetime: 20000
        borrowConnectionTimeout: 30
        loginTimeout: 30
        maintenanceInterval: 60
        maxIdleTime: 60

      test2:
        jdbcurl: jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
        minPoolSize: 3
        maxPoolSize: 25
        maxLifetime: 20000
        borrowConnectionTimeout: 30
        loginTimeout: 30
        maintenanceInterval: 60
        maxIdleTime: 60

mybatis:
    mapper-locations: classpath:mapper/*.xml

#设置静态资源路径,多个以逗号分隔
spring.resources.static-locations: classpath:static/,file:static/

# 日志配置
logging:
  level:
    czs: debug
    org.springframework: WARN
    org.spring.springboot.dao: debug

 

5.实体类

 ps.使用lombok插件挺方便的~   id数据库主键自增

复制代码

@Data
public class User {
    private Integer id;
    private String name;
    private long age;
}

复制代码

 

6.mapper接口

UserMapper1:

复制代码

public interface UserMapper1 {
    // 查询语句
    @Select("SELECT * FROM users WHERE NAME = #{name}")
    User findByName(@Param("name") String name);

    // 添加
    @Insert("INSERT INTO users(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);
}

复制代码

 

UserMapper2:

复制代码

public interface UserMapper2 {
    // 查询语句
    @Select("SELECT * FROM users WHERE NAME = #{name}")
    User findByName(@Param("name") String name);

    // 添加
    @Insert("INSERT INTO users(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);
}

复制代码

 

7.service

ManyService1:

复制代码

@Service
public class ManyService1 {

    @Autowired
    private UserMapper1 userMapper1;
    @Autowired
    private UserMapper2 userMapper2;

 /*   @Transactional(transactionManager = "test1TransactionManager",rollbackFor = Exception.class)
    public int insert(String name, Integer age) {
        int i = userMapper1.insert(name, age);
        System.out.println("userMapper1.insert结束~ :" + i);
       // int a = 1 / 0;//手动异常
        return i;
    }*/

    // 开启事务,由于使用jta+atomikos解决分布式事务,所以此处不必再指定事务
    @Transactional
    public int insert(String name, Integer age) {
        int insert = userMapper1.insert(name, age);
        //int i = 1 / age;// 赋值age为0故意引发事务
        return insert;
    }

    //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom3&age=2
    // 开启事务,由于使用jta+atomikos解决分布式事务,所以此处不必再指定事务
    @Transactional
    public int insertDb1AndDb2(String name, Integer age) {
        int insert = userMapper1.insert(name, age);
        int insert2 = userMapper2.insert(name, age);
        int i = 1 / age;// 赋值age为0故意引发事务
        return insert + insert2;
    }


}

复制代码

ManyService2:

复制代码

@Service
public class ManyService2 {

    @Autowired
    private UserMapper2 userMapper2;

    @Transactional(transactionManager = "test2TransactionManager",rollbackFor = Exception.class)
    public int insert(String name, Integer age) {
        int i = userMapper2.insert(name, age);
        System.out.println("userMapper2.insert结束~ :" + null);
        int a = 1 / 0;//手动异常
        return i;
    }

}

复制代码

 

8.Controller

复制代码

@RestController
public class ManyController {

    @Autowired
    private ManyService1 manyService1;

    @Resource
    private ManyService2 manyService2;

    @RequestMapping(value = "datasource1")
    public int datasource1(String name, Integer age) {
        return manyService1.insert(name, age);
    }

    @RequestMapping(value = "datasource2")
    public int datasource2(String name, Integer age) {
        return manyService2.insert(name, age);
    }

    /**
     * @Param:
     * @Description: 这里测试两个service两个数据源的事务(不加上atomikos插件的情况下测试,
     *使用DataSource1Config和DataSource2Config 两个配置类, 关闭DBConfig1, DBConfig2和MyBatisConfig1, MyBatisConfig1两个类)
     * @Author: zyf    2019/5/10
     */
    //http://localhost:8080/manyDatasource/testManyTrans?name=tom4&age=2
    @RequestMapping(value = "testManyTrans")
    public int testManyTrans(String name, Integer age) {
        int i = 0;
        int i1 = manyService1.insert(name, age);
        System.out.println("manyService1.insert :" + i1);

        /*
        第二个事务中会手动造成一个异常~,
        但是第一个事务执行完毕了,保存到了数据库
        */
        int i2 = manyService2.insert(name, age);
        System.out.println("manyService2.insert :" + i2);
        return i;
    }


    /**
     * @Param:
     * @Description: 这里测试使用atomikos插件测试多数据源事务
     * @Author: zyf    2019/5/10
     */
    //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom5&age=2
    //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom6&age=0  //测试除数为0后的事务管理
    @RequestMapping(value = "insertDb1AndDb2")
    public int insertDb1AndDb2(String name, Integer age) {
        return manyService1.insertDb1AndDb2(name, age);
    }


}

复制代码

 

9.配置数据源(*******重点总是在最后********)

DBConfig1:
@Data
@ConfigurationProperties(prefix = "spring.datasource.test1") // 注意这个前缀要和application.yml文件的前缀一样
public class DBConfig1 {
   // @Value("${mysql.datasource.test1.jdbcurl}")
   //@Value("${jdbcurl}")
    private String jdbcurl;
    //private String url;
    // 比如这个url在properties中是这样子的mysql.datasource.test1.username = root
    private String username;
    private String password;
    private int minPoolSize;
    private int maxPoolSize;
    private int maxLifetime;
    private int borrowConnectionTimeout;
    private int loginTimeout;
    private int maintenanceInterval;
    private int maxIdleTime;
    private String testQuery;
}
DBConfig2:

@Data
@ConfigurationProperties(prefix = "spring.datasource.test2")// 注意这个前缀要和application.yml文件的前缀一样
public class DBConfig2 {
    //@Value("${spring.datasource.test2.jdbcurl}")
    //@Value("${jdbcurl}")
    //private String url;
    private String jdbcurl;
    private String username;
    private String password;
    private int minPoolSize;
    private int maxPoolSize;
    private int maxLifetime;
    private int borrowConnectionTimeout;
    private int loginTimeout;
    private int maintenanceInterval;
    private int maxIdleTime;
    private String testQuery;

}

上面两个配置类作用: 将application.yml配置文件中配置自动封装到实体类字段中,然后赋值给atomikos类型的数据源.(下面两个具体配置数据源)

MyBatisConfig1:

复制代码

// 配置数据源
    //@Bean(name = "testDataSource")  //test1DataSource
    @Bean(name = "test1DataSource")  //test1DataSource
    public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        //mysqlXaDataSource.setUrl(testConfig.getUrl());
        mysqlXaDataSource.setUrl(testConfig.getJdbcurl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(testConfig.getPassword());
        mysqlXaDataSource.setUser(testConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        // 将本地事务注册到创 Atomikos全局事务
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("test1DataSource");

        xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(testConfig.getTestQuery());
        return xaDataSource;
    }

    @Bean(name = "test1SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean(name = "test1SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(
            @Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
MyBatisConfig2 :
@Configuration
@MapperScan(basePackages = "czs.mapper2", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class MyBatisConfig2 {

    // 配置数据源
    @Bean(name = "test2DataSource")
    public DataSource testDataSource(DBConfig2 testConfig) throws SQLException {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        //mysqlXaDataSource.setUrl(testConfig.getUrl());
        mysqlXaDataSource.setUrl(testConfig.getJdbcurl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(testConfig.getPassword());
        mysqlXaDataSource.setUser(testConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("test2DataSource");

        xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(testConfig.getTestQuery());
        return xaDataSource;
    }

    @Bean(name = "test2SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean(name = "test2SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(
            @Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
 

 

 

10.测试

http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom5&age=2   
结果: test1和test2数据库都插入数据~
http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom6&age=0   (两个insert操作后,手动异常)
结果: test1和test2数据库都未插入数据~

 GitHub传送门: https://github.com/ColoZhu/springbootmanyDatasource

 

备注说明:

  1.     /** min-pool-size 最小连接数 **/
  2.     private int minPoolSize;
  3.     /** max-pool-size 最大连接数 **/
  4.     private int maxPoolSize;
  5.     /** max-lifetime 连接最大存活时间 **/
  6.     private int maxLifetime;
  7.     /** borrow-connection-timeout 获取连接失败重新获等待最大时间,在这个时间内如果有可用连接,将返回 **/
  8.     private int borrowConnectionTimeout;
  9.     /** login-timeout java数据库连接池,最大可等待获取datasouce的时间 **/
  10.     private int loginTimeout;
  11.     /** maintenance-interval 连接回收时间 **/
  12.     private int maintenanceInterval;
  13.     /** max-idle-time 最大闲置时间,超过最小连接池连接的连接将将关闭 **/
  14.     private int maxIdleTime;
  15.     /** test-query 测试SQL **/
  16.     private String testQuery;

可能存在的问题:

1、using the Connector/J connection property ‘autoReconnect=true’ to avoid this problem.    https://blog.csdn.net/zljjava/article/details/7996091

日志:using the Connector/J connection property ‘autoReconnect=true’ to avoid this problem.  

\ 订阅

com.mysql.jdbc.CommunicationsException: The last packet successfully received from the server was58129 seconds ago.The last packet sent successfully to the server was 58129 seconds ago, which is longer than the server configured value of ‘wait_timeout’. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property ‘autoReconnect=true’ to avoid this problem.

 

查了一下,原来是mysql超时设置的问题
如 果连接闲置8小时 (8小时内没有进行数据库操作), mysql就会自动断开连接, 要重启tomcat. 

 

 

解决办法:

 

 

    一种. 如果不用hibernate的话, 则在 connection url中加参数: autoReconnect=true

jdbc.url=jdbc:mysql://ipaddress:3306/database?autoReconnect=true&amp;autoReconnectForPools=true

 

    二种。用hibernate的话, 加如下属性: 
        <property name=”connection.autoReconnect”>true</property>
        <property name=”connection.autoReconnectForPools”>true</property>
        <property name=”connection.is-connection-validation-required”>true</property>

 

    三。要是还用c3p0连接池: 
        <property name=”hibernate.c3p0.acquire_increment”>1</property> 
        <property name=”hibernate.c3p0.idle_test_period”>0</property> 
        <property name=”hibernate.c3p0.timeout”>0</property>
        <property name=”hibernate.c3p0.validate”>true</property>

 

 

 四。最不好的解决方案

 

使用Connector/J连接MySQL数据库,程序运行较长时间后就会报以下错误:

Communications link failure,The last packet successfully received from the server was *** millisecond ago.The last packet successfully sent to the server was ***  millisecond ago。

其中错误还会提示你修改wait_timeout或是使用Connector/J的autoReconnect属性避免该错误。

后来查了一些资料,才发现遇到这个问题的人还真不少,大部分都是使用连接池方式时才会出现这个问题,短连接应该很难出现这个问题。这个问题的原因:

MySQL服务器默认的“wait_timeout”是28800秒即8小时,意味着如果一个连接的空闲时间超过8个小时,MySQL将自动断开该 连接,而连接池却认为该连接还是有效的(因为并未校验连接的有效性),当应用申请使用该连接时,就会导致上面的报错。

1.按照错误的提示,可以在JDBC URL中使用autoReconnect属性,实际测试时使用了autoReconnect=true& failOverReadOnly=false,不过并未起作用,使用的是5.1版本,可能真像网上所说的只对4之前的版本有效。

2.没办法,只能修改MySQL的参数了,wait_timeout最大为31536000即1年,在my.cnf中加入:

[mysqld]

wait_timeout=31536000

interactive_timeout=31536000

重启生效,需要同时修改这两个参数。

=============================================================================================================

 

MySQL数据库连接超时(wait_timeout)问题的处理

MySQL Linux Windows JDBC 活动 

    想必大家在用MySQL时都会遇到连接超时的问题,如下图所示: 
 
    就是这个异常(com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:Communications link failure Last packet sent to the server was X ms ago),是由于MySQL服务在长时间不连接之后断开了,断开之后的首次请求会抛出这个异常。那么既然是连接超时的问题,就要去MySQL中探究一下连接时间是怎么控制的。打开MySQL的控制台,运行:show variables like ‘%timeout%’,查看和连接时间有关的MySQL系统变量,得到如下结果: 
 
    其中wait_timeout就是负责超时控制的变量,其时间为长度为28800s,就是8个小时,那么就是说MySQL的服务会在操作间隔8小时后断开,需要再次重连。也有用户在URL中使用jdbc.url=jdbc:mysql://localhost:3306/nd?autoReconnect=true来使得连接自动恢复,当然了,这是可以的,不过是MySQL4及其以下版本适用。MySQL5中已经无效了,必须调整系统变量来控制了。MySQL5手册中对两个变量有如下的说明: 
    interactive_timeout:服务器关闭交互式连接前等待活动的秒数。交互式客户端定义为在mysql_real_connect()中使用CLIENT_INTERACTIVE选项的客户端。又见wait_timeout 
    wait_timeout:服务器关闭非交互连接之前等待活动的秒数。在线程启动时,根据全局wait_timeout值或全局interactive_timeout值初始化会话wait_timeout值,取决于客户端类型(由mysql_real_connect()的连接选项CLIENT_INTERACTIVE定义),又见interactive_timeout 
    如此看来,两个变量是共同控制的,那么都必须对他们进行修改了。继续深入这两个变量wait_timeout的取值范围是1-2147483(Windows),1-31536000(linux),interactive_time取值随wait_timeout变动,它们的默认值都是28800。 
    MySQL的系统变量由配置文件控制,当配置文件中不配置时,系统使用默认值,这个28800就是默认值。要修改就只能在配置文件里修改。Windows下在%MySQL HOME%/bin下有mysql.ini配置文件,打开后在如下位置添加两个变量,赋值。(这里修改为388000) 
    保存退出,重启mysql服务,一定是重启系统服务。便可看到修改结果: 
 
    Linux系统下的配置文件为/etc/my.cnf。需要多说一点的是:windows下的配置文件具体是哪个需要从windows系统服务中找到mysql,打开属性,看“可执行文件路径”里面的参数值,因为它可能是my.cnf而不是my.ini,这是由于安装时的设置,我们可能会忽略它。 
 
    至此,修改完成,那么连接超时的问题从数据库上就解决了。当然在编程时也可以使用连接池设置超时时间,配置相对简单。但修改数据库更为本质一些。 
    个人见解。希望对使用者有用。

 

2、Atomikos数据连接池源码,弄清testQuery

作者:fbysss
msn:jameslastchina@hotmail.com
blog:blog.csdn.net/fbysss
声明:本文由fbysss原创,转载请注明出处
关键字:Atomikos数据连接池

 

前言

Atomikos数据连接池,国内有一些应用,但testQuery这个属性,在网上均是简单配置,并没有做特殊说明。通过对Atomikos源码的分析,发现这里很有学问。

 

分析

我们使用的数据源是AtomikosDataSourceBean,在其doInit方法中,会调用AtomikosXAConnectionFactory的createPooledConnection方法,该方法会返回一个AtomikosXAPooledConnection连接。

而AtomikosXAPooledConnection是AbstractXPooledConnection的一个子类,AbstractXPooledConnection中,在调用createConnectionProxy时,会调用testUnderlyingConnection方法,用于进行测试。

在AtomikosDataSourceBean的getConnection时,调用connectionPool的borrowConnection方法从而调用AbstractXPooledConnection中的createConnectionProxy,从而调用testUnderlyingConnection方法。

可以看testUnderlyingConnection中的关键代码,一旦设置了testQuery,每次getConnection的时候,就会连接查询一次(通过jprofiler也可以检测出来):

 

 

[java] view plaincopyprint?

 

 

如果失败,抛出CreateConnectionException异常。

 

注意ConnectionPool的borrowConnection方法,其中有一段:

 

 

[java] view plaincopyprint?

可以看到,其做的事情,就是遍历连接池中的连接,一个一个的测试。注意ret = xpc.createConnectionProxy ( hmsg ),一旦该方法抛出CreateConnectionException,就执行it.remove();即将该连接从连接池中删除。

 

如果设置了testQuery属性,每次获取连接时testQuery,能够保证应用服务器启动之后,与数据库连接暂时中断之后,能够在下一次请求时,自动重新建立连接。

连接池是如何自动建立连接的呢?其实就是简单的把无效的连接一个一个删掉,直到全部删光了,池里面没有有效(poolAvailableSize==0,是根据连接的isTerminated状态来判断的,而不是是否被重置过)的连接了,这样根据连接池的机制,就会调用growPool方法去请求新的连接。

但是这样有性能消耗,而且还不小。对于网站展示部分,不需要实时去检测,可以考虑采用定时检测的方法:

 

 

n 首先,保证testQuery为空,不配置。

n 创建一个DbPoolMonitorService类,实现ApplicationContextAware接口,这样应用启动时,会自动注入ApplicationContext对象。这样,可以在service中,调用getBean方法,获取AtomikosDataSourceBean的实例,

AtomikosDataSourceBean ads = (AtomikosDataSourceBean)ctx.getBean(“jtaDataSource”);

然后,ads中能够获得minPoolSize/maxPoolSize/availablePoolSize/totalPoolSize等属性,可以做一个界面来监测数据连接池的使用和配置情况,甚至可以动态修改这些属性。

n 做一个定时器,调用一个DbPoolMonitorService定期去手工test。使用SELECT 1 语句即可。

n 注意:try 部分ads.setTestQuery (“SELECT 1”);然后调用BaseDao的实例去执行一句话,依然可以使”SELECT 1” 。finally部分ads.setTestQuery(null),以保证不对其他部分造成后续影响。

n 可根据用户访问量,来决定定时周期,一般10分钟左右即可。这样,避免了每次获取连接去做一次检测操作,又能够将故障限制在一定时间范围内。是一个较好的折衷做法。

 

 

补充:我们再看ret = xpc.createConnectionProxy ( hmsg )这句话,如果这时候DB和WebAppServer的网络链路已经正常,DB正常运行,返回将不是空,会退出循环。这样,如果连接池中的连接是多个,则只会生成1个新的连接,如果要保证连接池机制的效果,需要在写监控程序的时候,去取得所有有效连接,循环test。