1. 程式人生 > >基於spring+mybatis+atomikos+jta實現分布式事務(1)

基於spring+mybatis+atomikos+jta實現分布式事務(1)

dto action 綁定 adapter lns url tab source private

本文介紹基於spring+mybatis+atomikos+jta實現分布式事務,分布式事務的實現方式基於配置文件,不同的mybatis mapper綁定在不同的數據源上,通過atomikos可實現分布式事務一致性。

版本:spring-3.2.9.RELEASE、mybatis-3.4.4、atomikos-4.0.5、jdk1.8

1,maven配置文件pom.xml如下:

    <!-- test -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <artifactId>hamcrest-core</artifactId>
                <groupId>org.hamcrest</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-all</artifactId>
        <version>1.3</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>1.9.5</version>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <artifactId>hamcrest-core</artifactId>
                <groupId>org.hamcrest</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>com.jayway.jsonpath</groupId>
        <artifactId>json-path</artifactId>
        <version>0.8.1</version>
        <scope>test</scope>
    </dependency>
    <!-- spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc-portlet</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>3.2.18.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-struts</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.4</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.1</version>
    </dependency>

    <!-- mysql-jdbc -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.27</version>
    </dependency>

    <!-- atomikos begin -->
    <dependency>
        <groupId>com.atomikos</groupId>
        <artifactId>transactions-jdbc</artifactId>
        <version>4.0.5</version>
    </dependency>
    <!-- atomikos end -->

    <!-- jta begig -->
    <dependency>
        <groupId>javax.transaction</groupId>
        <artifactId>jta</artifactId>
        <version>1.1</version>
    </dependency>
    <!-- jta end -->
    <!-- jackson -->
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>1.9.13</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-core-asl</artifactId>
        <version>1.9.13</version>
    </dependency>
    <!-- jackson end -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>

2,數據庫連接配置文件:jdbc.properties

jdbc.xaDataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
validationQuery=select 1

ds1.jdbc.url=jdbc:mysql://xx.xx.xx.xx:3306/test1?characterEncoding=UTF-8
ds1.jdbc.username=xxxx
ds1.jdbc.password=xxxx

ds2.jdbc.url=jdbc:mysql://xx.xx.xx.xx:3306/test2?characterEncoding=UTF-8

ds2.jdbc.username=xxxx
ds2.jdbc.password=xxxx

3,atomikos配置文件:jta.properties

com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name=tm.release.out
com.atomikos.icatch.log_base_name=tm.releaselog
com.atomikos.icatch.tm_unique_name=com.atomikos.spring.jdbc.tm.release

com.atomikos.icatch.console_log_level=INFO

4,spring-datasource-jta.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:jdbc.properties</value>
</list>
</property>
</bean>
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<bean class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown" value="true"></property>
</bean>
</property>
<property name="userTransaction">
<bean class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300"></property>
</bean>
</property>
</bean>

${ds1.jdbc.url} ${ds1.jdbc.username} ${ds1.jdbc.password} ${ds2.jdbc.url} ${ds2.jdbc.username} ${ds2.jdbc.password} true ## 5,spring-mybatis.xml classpath:mybatis/model/*.xml classpath:mybatis/model2/*.xml ## 6,編寫mybatis.model代碼,HelloDO.java、HelloMapper.java、HelloMapper.xml HelloDO.java package mybatis.model; public class HelloDO { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } HelloMapper.java package mybatis.model; import org.springframework.stereotype.Repository; @Repository(value="helloMapper") public interface HelloMapper { public void insert(HelloDO hello); } HelloMapper.xml insert into HELLO(ID,NAME) values(#{id},#{name}) ## 7,編寫mybatis.model2代碼,HelloDO.java、HelloMapper.java、HelloMapper.xml HelloDO.java package mybatis.model2; public class HelloDO { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } HelloMapper.java package mybatis.model2; import org.springframework.stereotype.Repository; @Repository(value="helloMapper2") public interface HelloMapper { public void insert(HelloDO hello); } HelloMapper.xml insert into HELLO(ID,NAME) values(#{id},#{name}) ## 8,編寫service類:mybatis.service.HelloWorldService.java package mybatis.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import mybatis.model.HelloDO; import mybatis.model.HelloMapper; @Service("helloWorldService") public class HelloWorldService { @Autowired private HelloMapper helloMapper; @Autowired private mybatis.model2.HelloMapper helloMapper2; public void addHello(HelloDO helloDO) { // TODO Auto-generated method stub helloMapper.insert(helloDO); } public void addHello2(mybatis.model2.HelloDO helloDO) { helloMapper2.insert(helloDO); } } ## 9,編寫controller類:mybatis.controler.HelloWorldController.java package mybatis.controller; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import mybatis.model.HelloDO; import mybatis.service.HelloWorldService; @Controller(value = "helloWorldController") @RequestMapping(value="/hello") public class HelloWorldControler { @Autowired private HelloWorldService helloWorldService; @Transactional @RequestMapping(value="/create", method = RequestMethod.POST, consumes = {"text/plain", "application/*"}) public @ResponseBody long createHello(@RequestBody HelloDO helloDO) { helloWorldService.addHello(helloDO); mybatis.model2.HelloDO helloDO2 = new mybatis.model2.HelloDO(); BeanUtils.copyProperties(helloDO, helloDO2); helloWorldService.addHello2(helloDO2); return helloDO.getId(); } } ## 10,編寫測試類:HelloWorldControllerTest.java package mybatis.controller; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectWriter; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import mybatis.model.HelloDO; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration({ "classpath:spring-datasource-jta.xml", "classpath:spring-mybatis.xml" }) public class HelloWorldControlerTest { @Autowired private WebApplicationContext context; private MockMvc mocMvc; @Before public void setUp() throws Exception { mocMvc = MockMvcBuilders.webAppContextSetup(context).build(); } @Test public void test() throws Exception { String data = "{\"id\":1,\"name\":\"abc\"}"; HelloDO hello = new HelloDO(); hello.setId(1L); hello.setName("abc"); ObjectMapper mapper = new ObjectMapper(); ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter(); java.lang.String requestJson = ow.writeValueAsString(hello); mocMvc.perform(post("/hello/create").contentType(MediaType.APPLICATION_JSON).content(data)).andExpect(status().isOk()); } } ## 11,至此,基於配置文件實現分布式事務一致性已經實現,下一篇文件將介紹通過代碼來動態切換數據源,並保持數據庫事務一致性。

基於spring+mybatis+atomikos+jta實現分布式事務(1)