提交 bffd4b6c 编写于 作者: Z zhaojun 提交者: ShardingSphere

move sharding-transaction-spring & boot-starter to spi-impl project. (#2111)

上级 b768413d
......@@ -70,11 +70,9 @@
<opentracing.version>0.30.0</opentracing.version>
<lombok.version>1.16.4</lombok.version>
<aspectjweaver.version>1.8.9</aspectjweaver.version>
<springframework.version>[4.3.6.RELEASE,5.0.0.M1)</springframework.version>
<spring-boot.version>[1.5.0.RELEASE,2.0.0.M1)</spring-boot.version>
<hibernate.version>5.3.7.Final</hibernate.version>
<junit.version>4.12</junit.version>
<hamcrest.version>1.3</hamcrest.version>
......@@ -224,6 +222,19 @@
<version>${opentracing.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springframework.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
......@@ -255,42 +266,6 @@
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springframework.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${springframework.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
......@@ -298,26 +273,6 @@
<version>${spring-boot.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${springframework.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
......
......@@ -30,7 +30,5 @@
<modules>
<module>sharding-jdbc-spring</module>
<module>sharding-jdbc-orchestration-spring</module>
<module>sharding-transaction-spring</module>
<module>sharding-transaction-spring-boot-starter</module>
</modules>
</project>
......@@ -36,10 +36,6 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
......
......@@ -17,19 +17,18 @@
package org.apache.shardingsphere.shardingjdbc.spring.boot.type;
import org.apache.shardingsphere.shardingjdbc.jdbc.adapter.AbstractDataSourceAdapter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import javax.sql.DataSource;
import static org.junit.Assert.assertFalse;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringBootRawDataSourceTest.class)
@ActiveProfiles("raw")
......@@ -37,10 +36,10 @@ import static org.junit.Assert.assertFalse;
public class SpringBootRawDataSourceTest {
@Resource
private DataSource datasource;
private ApplicationContext context;
@Test
@Test(expected = NoSuchBeanDefinitionException.class)
public void assertDataSource() {
assertFalse(datasource instanceof AbstractDataSourceAdapter);
context.getBean(DataSource.class);
}
}
......@@ -36,10 +36,6 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
......
......@@ -17,19 +17,18 @@
package org.apache.shardingsphere.shardingjdbc.spring.boot.type;
import org.apache.shardingsphere.shardingjdbc.jdbc.adapter.AbstractDataSourceAdapter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import javax.sql.DataSource;
import static org.junit.Assert.assertFalse;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringBootRawDataSourceTest.class)
@ActiveProfiles("raw")
......@@ -37,10 +36,10 @@ import static org.junit.Assert.assertFalse;
public class SpringBootRawDataSourceTest {
@Resource
private DataSource datasource;
private ApplicationContext context;
@Test
@Test(expected = NoSuchBeanDefinitionException.class)
public void assertDataSource() {
assertFalse(datasource instanceof AbstractDataSourceAdapter);
context.getBean(DataSource.class);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-spring</artifactId>
<version>4.0.0-RC1-SNAPSHOT</version>
</parent>
<artifactId>sharding-transaction-spring-boot-starter</artifactId>
<name>${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-transaction-spring</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</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</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.spring.boot;
import org.apache.shardingsphere.transaction.aspect.ShardingTransactionalAspect;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.ClassUtils;
import javax.sql.DataSource;
import java.util.Arrays;
/**
* Spring boot sharding transaction configuration.
*
* @author yangyi
*/
@Configuration
@NoArgsConstructor
public class ShardingTransactionConfiguration {
/**
* Build sharding transaction aspect bean.
*
* @return sharding transaction aspect bean
*/
@Bean
public ShardingTransactionalAspect shardingTransactionalAspect() {
return new ShardingTransactionalAspect();
}
/**
* Build hibernate transaction manager.
*
* @param transactionManagerCustomizers transaction manager customizers
* @return jpa transaction manager
*/
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
@Conditional(HibernateEntityManagerCondition.class)
public PlatformTransactionManager jpaTransactionManager(final ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
JpaTransactionManager result = new JpaTransactionManager();
if (null != transactionManagerCustomizers.getIfAvailable()) {
transactionManagerCustomizers.getIfAvailable().customize(result);
}
return result;
}
/**
* Build datasource transaction manager.
*
* @param dataSource data source
* @param transactionManagerCustomizers transaction manager customizers
* @return datasource transaction manager
*/
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager dataSourceTransactionManager(final DataSource dataSource, final ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
DataSourceTransactionManager result = new DataSourceTransactionManager(dataSource);
if (null != transactionManagerCustomizers.getIfAvailable()) {
transactionManagerCustomizers.getIfAvailable().customize(result);
}
return result;
}
@NoArgsConstructor
static class HibernateEntityManagerCondition extends SpringBootCondition {
private static final String[] CLASS_NAMES = new String[]{"org.hibernate.ejb.HibernateEntityManager", "org.hibernate.jpa.HibernateEntityManager"};
@Override
public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
Builder message = ConditionMessage.forCondition("HibernateEntityManager", new Object[0]);
for (String each : CLASS_NAMES) {
if (ClassUtils.isPresent(each, context.getClassLoader())) {
return ConditionOutcome.match(message.found("class").items(Style.QUOTE, new Object[]{each}));
}
}
return ConditionOutcome.noMatch(message.didNotFind("class", "classes").items(Style.QUOTE, Arrays.asList(CLASS_NAMES)));
}
}
}
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.shardingsphere.transaction.spring.boot.ShardingTransactionConfiguration
\ No newline at end of file
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
provides: sharding-transaction-spring-boot-starter
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.spring.boot;
import org.apache.shardingsphere.transaction.aspect.ShardingTransactionalAspect;
import org.apache.shardingsphere.transaction.spring.boot.fixture.ShardingTransactionalTestService;
import org.apache.shardingsphere.transaction.spring.boot.util.TransactionManagerMockUtil;
import org.apache.shardingsphere.core.exception.ShardingException;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.apache.shardingsphere.transaction.core.TransactionTypeHolder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.sql.Statement;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ShardingTransactionalSpringBootTest.class)
@SpringBootApplication
@ComponentScan("org.apache.shardingsphere.transaction.spring.boot.fixture")
public class ShardingTransactionalSpringBootTest {
@Autowired
private ShardingTransactionalTestService testService;
@Autowired
private ShardingTransactionalAspect aspect;
private final Statement statement = mock(Statement.class);
private final JpaTransactionManager jpaTransactionManager = mock(JpaTransactionManager.class);
private final DataSourceTransactionManager dataSourceTransactionManager = mock(DataSourceTransactionManager.class);
@Before
public void setUp() throws SQLException {
TransactionManagerMockUtil.initTransactionManagerMock(statement, jpaTransactionManager, dataSourceTransactionManager);
}
@After
public void tearDown() {
aspect.setEnvironment(new DataSource[]{});
}
@Test
public void assertChangeTransactionTypeToXA() {
testService.testChangeTransactionTypeToXA();
assertThat(TransactionTypeHolder.get(), is(TransactionType.LOCAL));
}
@Test
public void assertChangeTransactionTypeToBASE() {
testService.testChangeTransactionTypeToBASE();
assertThat(TransactionTypeHolder.get(), is(TransactionType.LOCAL));
}
@Test
public void assertChangeTransactionTypeToLocal() {
TransactionTypeHolder.set(TransactionType.XA);
testService.testChangeTransactionTypeToLOCAL();
assertThat(TransactionTypeHolder.get(), is(TransactionType.LOCAL));
}
@Test
public void assertChangeTransactionTypeInClass() {
testService.testChangeTransactionTypeInClass();
assertThat(TransactionTypeHolder.get(), is(TransactionType.LOCAL));
}
@Test(expected = ShardingException.class)
public void assertChangeTransactionTypeForProxyWithIllegalTransactionManager() throws SQLException {
TransactionManagerMockUtil.testChangeProxyTransactionTypeToLOCAL(testService, aspect, mock(PlatformTransactionManager.class));
}
@Test(expected = ShardingException.class)
public void assertChangeTransactionTypeForProxyFailed() throws SQLException {
when(statement.execute(anyString())).thenThrow(new SQLException("test switch exception"));
TransactionManagerMockUtil.testChangeProxyTransactionTypeToLOCAL(testService, aspect, dataSourceTransactionManager);
}
@Test
public void assertChangeTransactionTypeToLOCALForProxy() throws SQLException {
when(statement.execute(anyString())).thenReturn(true);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToLOCAL(testService, aspect, dataSourceTransactionManager);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToLOCAL(testService, aspect, jpaTransactionManager);
verify(statement, times(2)).execute("SCTL:SET TRANSACTION_TYPE=LOCAL");
}
@Test
public void assertChangeTransactionTypeToXAForProxy() throws SQLException {
when(statement.execute(anyString())).thenReturn(true);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToXA(testService, aspect, dataSourceTransactionManager);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToXA(testService, aspect, jpaTransactionManager);
verify(statement, times(2)).execute("SCTL:SET TRANSACTION_TYPE=XA");
}
@Test
public void assertChangeTransactionTypeToBASEForProxy() throws SQLException {
when(statement.execute(anyString())).thenReturn(true);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToBASE(testService, aspect, dataSourceTransactionManager);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToBASE(testService, aspect, jpaTransactionManager);
verify(statement, times(2)).execute("SCTL:SET TRANSACTION_TYPE=BASE");
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.spring.boot.fixture;
import org.apache.shardingsphere.transaction.annotation.ShardingTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Service
@Component
@ShardingTransactionType(TransactionType.XA)
public class ShardingTransactionalTestService {
@ShardingTransactionType
public void testChangeTransactionTypeToLOCAL() {
}
@ShardingTransactionType(TransactionType.XA)
public void testChangeTransactionTypeToXA() {
}
@ShardingTransactionType(TransactionType.BASE)
public void testChangeTransactionTypeToBASE() {
}
public void testChangeTransactionTypeInClass() {
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.spring.boot.util;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.shardingsphere.transaction.aspect.ShardingTransactionalAspect;
import org.apache.shardingsphere.transaction.spring.boot.fixture.ShardingTransactionalTestService;
import org.hibernate.engine.spi.SessionImplementor;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TransactionManagerMockUtil {
/**
* Init transaction manager mock.
*
* @param statement mock statement
* @param jpaTransactionManager mock jpa transaction manager
* @param dataSourceTransactionManager mock dataSource transaction manager
* @throws SQLException SQL exception
*/
public static void initTransactionManagerMock(
final Statement statement, final JpaTransactionManager jpaTransactionManager, final DataSourceTransactionManager dataSourceTransactionManager) throws SQLException {
DataSource dataSource = mock(DataSource.class);
Connection connection = mock(Connection.class);
EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class);
EntityManager entityManager = mock(EntityManager.class);
SessionImplementor sessionImplementor = mock(SessionImplementor.class);
when(dataSource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
when(sessionImplementor.connection()).thenReturn(connection);
when(entityManager.unwrap(SessionImplementor.class)).thenReturn(sessionImplementor);
when(entityManagerFactory.createEntityManager()).thenReturn(entityManager);
when(jpaTransactionManager.getEntityManagerFactory()).thenReturn(entityManagerFactory);
when(dataSourceTransactionManager.getDataSource()).thenReturn(dataSource);
}
/**
* Test change proxy transaction type to LOCAL with specified transaction manager.
*
* @param testService sharding transaction test service
* @param aspect sharding transaction aspect
* @param transactionManager specified transaction manager
* @throws SQLException SQL exception
*/
public static void testChangeProxyTransactionTypeToLOCAL(
final ShardingTransactionalTestService testService, final ShardingTransactionalAspect aspect, final PlatformTransactionManager transactionManager) throws SQLException {
aspect.setTransactionManager(transactionManager);
aspect.setEnvironment(getProxyDataSource());
testService.testChangeTransactionTypeToLOCAL();
}
/**
* Test change proxy transaction type to XA with specified transaction manager.
*
* @param testService sharding transaction test service
* @param aspect sharding transaction aspect
* @param transactionManager specified transaction manager
* @throws SQLException SQL exception
*/
public static void testChangeProxyTransactionTypeToXA(
final ShardingTransactionalTestService testService, final ShardingTransactionalAspect aspect, final PlatformTransactionManager transactionManager) throws SQLException {
aspect.setTransactionManager(transactionManager);
aspect.setEnvironment(getProxyDataSource());
testService.testChangeTransactionTypeToXA();
}
/**
* Test change proxy transaction type to BASE with specified transaction manager.
*
* @param testService sharding transaction test service
* @param aspect sharding transaction aspect
* @param transactionManager specified transaction manager
* @throws SQLException SQL exception
*/
public static void testChangeProxyTransactionTypeToBASE(
final ShardingTransactionalTestService testService, final ShardingTransactionalAspect aspect, final PlatformTransactionManager transactionManager) throws SQLException {
aspect.setTransactionManager(transactionManager);
aspect.setEnvironment(getProxyDataSource());
testService.testChangeTransactionTypeToBASE();
}
private static DataSource[] getProxyDataSource() throws SQLException {
DataSource dataSource = mock(DataSource.class);
Connection connection = mock(Connection.class);
DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);
when(databaseMetaData.getDatabaseProductVersion()).thenReturn("5.6.0-Sharding-Proxy x.x.x");
when(connection.getMetaData()).thenReturn(databaseMetaData);
when(dataSource.getConnection()).thenReturn(connection);
return new DataSource[] {dataSource};
}
}
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
spring.jta.enabled=true
<?xml version="1.0"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%-5level] %d{HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.apache.shardingsphere" level="warn" additivity="false">
<appender-ref ref="console"/>
</logger>
<root>
<level value="error" />
<appender-ref ref="console" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-spring</artifactId>
<version>4.0.0-RC1-SNAPSHOT</version>
</parent>
<artifactId>sharding-transaction-spring</artifactId>
<name>${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-transaction-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
</dependencies>
</project>
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction;
/**
* Target environment of switch the transaction type.
*
* @author yangyi
*/
public enum ShardingEnvironment {
JDBC, PROXY
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.annotation;
import org.apache.shardingsphere.transaction.core.TransactionType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Sharding transactional annotation.
*
* @author yangyi
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ShardingTransactionType {
/**
* Sharding transaction type, include LOCAL, XA, BASE.
* default LOCAL.
*
* @return Sharding transaction type
*/
TransactionType value() default TransactionType.LOCAL;
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.aspect;
import org.apache.shardingsphere.transaction.ShardingEnvironment;
import org.apache.shardingsphere.transaction.annotation.ShardingTransactionType;
import org.apache.shardingsphere.transaction.handler.DataSourceTransactionManagerHandler;
import org.apache.shardingsphere.transaction.handler.JpaTransactionManagerHandler;
import org.apache.shardingsphere.transaction.handler.TransactionManagerHandler;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.shardingsphere.core.exception.ShardingException;
import org.apache.shardingsphere.transaction.core.TransactionTypeHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
/**
* Sharding transaction aspect.
*
* @author yangyi
*/
@Aspect
@Component
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public final class ShardingTransactionalAspect {
private static final String PROXY_TAG = "Sharding-Proxy";
private TransactionManagerHandler transactionManagerHandler;
private ShardingEnvironment environment;
/**
* Inject spring transaction manager.
* This transaction manager required when Switch transaction type for Sharding-Proxy.
*
* @param transactionManager spring transaction manager
*/
@Autowired
public void setTransactionManager(final PlatformTransactionManager transactionManager) {
setTransactionManagerHandler(transactionManager);
}
/**
* Analyze data source type to judge environment of sharding sphere.
*
* @param dataSources data sources array
*/
@Autowired
public void setEnvironment(final DataSource[] dataSources) {
environment = null != dataSources && isConnectToProxy(dataSources) ? ShardingEnvironment.PROXY : ShardingEnvironment.JDBC;
}
/**
* Sharding transactional AOP pointcut.
*/
@Pointcut("@annotation(org.apache.shardingsphere.transaction.annotation.ShardingTransactionType) || @within(org.apache.shardingsphere.transaction.annotation.ShardingTransactionType)")
public void shardingTransactionalPointCut() {
}
@Before(value = "shardingTransactionalPointCut()")
public void setTransactionTypeBeforeTransaction(final JoinPoint joinPoint) {
ShardingTransactionType shardingTransactionType = getAnnotation(joinPoint);
switch (environment) {
case JDBC:
TransactionTypeHolder.set(shardingTransactionType.value());
break;
case PROXY:
transactionManagerHandler.switchTransactionType(shardingTransactionType.value());
break;
default:
}
}
@After(value = "shardingTransactionalPointCut()")
public void cleanTransactionTypeAfterTransaction(final JoinPoint joinPoint) {
switch (environment) {
case JDBC:
TransactionTypeHolder.clear();
break;
case PROXY:
transactionManagerHandler.unbindResource();
break;
default:
}
}
private void setTransactionManagerHandler(final PlatformTransactionManager transactionManager) {
switch (TransactionManagerType.getTransactionManagerTypeByClassName(transactionManager.getClass().getName())) {
case DATASOURCE:
transactionManagerHandler = new DataSourceTransactionManagerHandler(transactionManager);
break;
case JPA:
transactionManagerHandler = new JpaTransactionManagerHandler(transactionManager);
break;
case UNSUPPORTED:
default:
throw new ShardingException(String.format("Switching transaction Type is unsupported for transaction manager %s", transactionManager.getClass().getName()));
}
}
private boolean isConnectToProxy(final DataSource[] dataSources) {
for (DataSource each : dataSources) {
try (Connection connection = each.getConnection()) {
DatabaseMetaData databaseMetaData = connection.getMetaData();
if (databaseMetaData.getDatabaseProductVersion().contains(PROXY_TAG)) {
return true;
}
} catch (SQLException ex) {
throw new ShardingException("Get databaseMetaData failed: ", ex);
}
}
return false;
}
private ShardingTransactionType getAnnotation(final JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
ShardingTransactionType result = method.getAnnotation(ShardingTransactionType.class);
if (null == result) {
result = method.getDeclaringClass().getAnnotation(ShardingTransactionType.class);
}
return result;
}
@RequiredArgsConstructor
private enum TransactionManagerType {
/**
* Spring {@code DataSourceTransactionManager}.
*/
DATASOURCE("org.springframework.jdbc.datasource.DataSourceTransactionManager"),
/**
* Spring {@code JpaTransactionManager}.
*/
JPA("org.springframework.orm.jpa.JpaTransactionManager"),
/**
* Other spring {@code PlatformTransactionManager}.
*/
UNSUPPORTED("");
@Getter
private final String className;
private static TransactionManagerType getTransactionManagerTypeByClassName(final String className) {
for (TransactionManagerType each : TransactionManagerType.values()) {
if (each.getClassName().equals(className)) {
return each;
}
}
return UNSUPPORTED;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.handler;
import org.apache.shardingsphere.core.exception.ShardingException;
import org.apache.shardingsphere.transaction.core.TransactionType;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Abstract transaction manager handler.
*
* @author yangyi
*/
public abstract class AbstractTransactionManagerHandler implements TransactionManagerHandler {
private static final String SET_TRANSACTION_TYPE_SQL = "SCTL:SET TRANSACTION_TYPE=%s";
@Override
public final void switchTransactionType(final TransactionType transactionType) {
Connection connection = getConnectionFromTransactionManager();
try (Statement statement = connection.createStatement()) {
statement.execute(String.format(SET_TRANSACTION_TYPE_SQL, transactionType.name()));
} catch (final SQLException ex) {
throw new ShardingException("Switch transaction type for sharding-proxy failed: ", ex);
}
}
/**
* Get physical connection which transaction manager will use.
*
* @return connection to Sharding-Proxy
*/
protected abstract Connection getConnectionFromTransactionManager();
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.handler;
import org.apache.shardingsphere.core.exception.ShardingException;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Datasource transaction manager handler.
*
* @author yangyi
*/
public final class DataSourceTransactionManagerHandler extends AbstractTransactionManagerHandler {
private final DataSourceTransactionManager transactionManager;
public DataSourceTransactionManagerHandler(final PlatformTransactionManager transactionManager) {
this.transactionManager = (DataSourceTransactionManager) transactionManager;
}
@Override
public void unbindResource() {
ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(transactionManager.getDataSource());
DataSourceUtils.releaseConnection(holder.getConnection(), transactionManager.getDataSource());
}
@Override
protected Connection getConnectionFromTransactionManager() {
try {
Connection result = transactionManager.getDataSource().getConnection();
TransactionSynchronizationManager.bindResource(transactionManager.getDataSource(), new ConnectionHolder(result));
return result;
} catch (final SQLException ex) {
throw new ShardingException("Could not open JDBC Connection before transaction", ex);
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.handler;
import org.hibernate.engine.spi.SessionImplementor;
import org.springframework.orm.jpa.EntityManagerFactoryInfo;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.CollectionUtils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import java.sql.Connection;
import java.util.Map;
/**
* Jpa transaction manager handler.
*
* @author yangyi
*/
public final class JpaTransactionManagerHandler extends AbstractTransactionManagerHandler {
private final JpaTransactionManager transactionManager;
public JpaTransactionManagerHandler(final PlatformTransactionManager transactionManager) {
this.transactionManager = (JpaTransactionManager) transactionManager;
}
@Override
public void unbindResource() {
EntityManagerHolder entityManagerHolder = (EntityManagerHolder) TransactionSynchronizationManager.unbindResourceIfPossible(transactionManager.getEntityManagerFactory());
EntityManagerFactoryUtils.closeEntityManager(entityManagerHolder.getEntityManager());
}
@Override
protected Connection getConnectionFromTransactionManager() {
EntityManager entityManager = createEntityManager();
Connection result = entityManager.unwrap(SessionImplementor.class).connection();
TransactionSynchronizationManager.bindResource(transactionManager.getEntityManagerFactory(), new EntityManagerHolder(entityManager));
return result;
}
private EntityManager createEntityManager() {
EntityManagerFactory entityManagerFactory = transactionManager.getEntityManagerFactory();
if (entityManagerFactory instanceof EntityManagerFactoryInfo) {
entityManagerFactory = ((EntityManagerFactoryInfo) entityManagerFactory).getNativeEntityManagerFactory();
}
Map<String, Object> properties = transactionManager.getJpaPropertyMap();
return !CollectionUtils.isEmpty(properties) ? entityManagerFactory.createEntityManager(properties) : entityManagerFactory.createEntityManager();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.handler;
import org.apache.shardingsphere.transaction.core.TransactionType;
/**
* Transaction manager handler interface.
*
* @author yangyi
*/
public interface TransactionManagerHandler {
/**
* Send switch transaction type SQL to Sharding-Proxy.
*
* @param transactionType transaction type
*/
void switchTransactionType(TransactionType transactionType);
/**
* Unbind resource.
*/
void unbindResource();
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="shardingTransactionAspect" class="org.apache.shardingsphere.transaction.aspect.ShardingTransactionalAspect"/>
</beans>
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction;
import org.apache.shardingsphere.transaction.handler.AllHandlerTests;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
AllHandlerTests.class,
ShardingTransactionalNameSpaceTest.class
})
public class AllTests {
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction;
import org.apache.shardingsphere.transaction.aspect.ShardingTransactionalAspect;
import org.apache.shardingsphere.transaction.fixture.ShardingTransactionalTestService;
import org.apache.shardingsphere.transaction.util.TransactionManagerMockUtil;
import org.apache.shardingsphere.core.exception.ShardingException;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.apache.shardingsphere.transaction.core.TransactionTypeHolder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.sql.Statement;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ContextConfiguration(locations = "classpath:shardingTransactionTest.xml")
public class ShardingTransactionalNameSpaceTest extends AbstractJUnit4SpringContextTests {
@Autowired
private ShardingTransactionalTestService testService;
@Autowired
private ShardingTransactionalAspect aspect;
private final Statement statement = mock(Statement.class);
private final JpaTransactionManager jpaTransactionManager = mock(JpaTransactionManager.class);
private final DataSourceTransactionManager dataSourceTransactionManager = mock(DataSourceTransactionManager.class);
@Before
public void setUp() throws SQLException {
TransactionManagerMockUtil.initTransactionManagerMock(statement, jpaTransactionManager, dataSourceTransactionManager);
}
@After
public void tearDown() {
aspect.setEnvironment(new DataSource[]{});
}
@Test
public void assertChangeTransactionTypeToXA() {
testService.testChangeTransactionTypeToXA();
assertThat(TransactionTypeHolder.get(), is(TransactionType.LOCAL));
}
@Test
public void assertChangeTransactionTypeToBASE() {
testService.testChangeTransactionTypeToBASE();
assertThat(TransactionTypeHolder.get(), is(TransactionType.LOCAL));
}
@Test
public void assertChangeTransactionTypeToLocal() {
TransactionTypeHolder.set(TransactionType.XA);
testService.testChangeTransactionTypeToLOCAL();
assertThat(TransactionTypeHolder.get(), is(TransactionType.LOCAL));
}
@Test
public void assertChangeTransactionTypeInClass() {
testService.testChangeTransactionTypeInClass();
assertThat(TransactionTypeHolder.get(), is(TransactionType.LOCAL));
}
@Test(expected = ShardingException.class)
public void assertChangeTransactionTypeForProxyWithIllegalTransactionManager() throws SQLException {
TransactionManagerMockUtil.testChangeProxyTransactionTypeToLOCAL(testService, aspect, mock(PlatformTransactionManager.class));
}
@Test(expected = ShardingException.class)
public void assertChangeTransactionTypeForProxyFailed() throws SQLException {
when(statement.execute(anyString())).thenThrow(new SQLException("test switch exception"));
TransactionManagerMockUtil.testChangeProxyTransactionTypeToLOCAL(testService, aspect, dataSourceTransactionManager);
}
@Test
public void assertChangeTransactionTypeToLOCALForProxy() throws SQLException {
when(statement.execute(anyString())).thenReturn(true);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToLOCAL(testService, aspect, dataSourceTransactionManager);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToLOCAL(testService, aspect, jpaTransactionManager);
verify(statement, times(2)).execute("SCTL:SET TRANSACTION_TYPE=LOCAL");
}
@Test
public void assertChangeTransactionTypeToXAForProxy() throws SQLException {
when(statement.execute(anyString())).thenReturn(true);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToXA(testService, aspect, dataSourceTransactionManager);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToXA(testService, aspect, jpaTransactionManager);
verify(statement, times(2)).execute("SCTL:SET TRANSACTION_TYPE=XA");
}
@Test
public void assertChangeTransactionTypeToBASEForProxy() throws SQLException {
when(statement.execute(anyString())).thenReturn(true);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToBASE(testService, aspect, dataSourceTransactionManager);
TransactionManagerMockUtil.testChangeProxyTransactionTypeToBASE(testService, aspect, jpaTransactionManager);
verify(statement, times(2)).execute("SCTL:SET TRANSACTION_TYPE=BASE");
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.fixture;
import org.apache.shardingsphere.transaction.annotation.ShardingTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Service
@Component
@ShardingTransactionType(TransactionType.XA)
public class ShardingTransactionalTestService {
@ShardingTransactionType
public void testChangeTransactionTypeToLOCAL() {
}
@ShardingTransactionType(TransactionType.XA)
public void testChangeTransactionTypeToXA() {
}
@ShardingTransactionType(TransactionType.BASE)
public void testChangeTransactionTypeToBASE() {
}
public void testChangeTransactionTypeInClass() {
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.handler;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
DataSourceTransactionManagerHandlerTest.class,
JpaTransactionManagerHandlerTest.class
})
public class AllHandlerTests {
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.handler;
import org.apache.shardingsphere.core.exception.ShardingException;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public final class DataSourceTransactionManagerHandlerTest {
@Mock
private DataSource dataSource;
@Mock
private DataSourceTransactionManager transactionManager;
private DataSourceTransactionManagerHandler dataSourceTransactionManagerHandler;
@Before
public void setUp() {
when(transactionManager.getDataSource()).thenReturn(dataSource);
dataSourceTransactionManagerHandler = new DataSourceTransactionManagerHandler(transactionManager);
}
@Test
public void assertSwitchTransactionTypeSuccess() throws SQLException {
Connection connection = mock(Connection.class);
Statement statement = mock(Statement.class);
when(dataSource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
dataSourceTransactionManagerHandler.switchTransactionType(TransactionType.XA);
verify(statement).execute(anyString());
TransactionSynchronizationManager.unbindResourceIfPossible(dataSource);
}
@Test(expected = ShardingException.class)
public void assertSwitchTransactionTypeFailExecute() throws SQLException {
Connection connection = mock(Connection.class);
Statement statement = mock(Statement.class);
when(dataSource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
when(statement.execute(anyString())).thenThrow(new SQLException("Mock send switch transaction type SQL failed"));
try {
dataSourceTransactionManagerHandler.switchTransactionType(TransactionType.XA);
} finally {
TransactionSynchronizationManager.unbindResourceIfPossible(dataSource);
}
}
@Test(expected = ShardingException.class)
public void assertSwitchTransactionTypeFailGetConnection() throws SQLException {
when(dataSource.getConnection()).thenThrow(new SQLException("Mock get connection failed"));
try {
dataSourceTransactionManagerHandler.switchTransactionType(TransactionType.XA);
} finally {
TransactionSynchronizationManager.unbindResourceIfPossible(dataSource);
}
}
@Test
public void assertUnbindResource() {
ConnectionHolder holder = mock(ConnectionHolder.class);
Connection connection = mock(Connection.class);
when(holder.getConnection()).thenReturn(connection);
TransactionSynchronizationManager.bindResource(dataSource, holder);
dataSourceTransactionManagerHandler.unbindResource();
assertNull(TransactionSynchronizationManager.getResource(dataSource));
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.handler;
import org.apache.shardingsphere.core.exception.ShardingException;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.hibernate.engine.spi.SessionImplementor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public final class JpaTransactionManagerHandlerTest {
@Mock
private JpaTransactionManager transactionManager;
@Mock
private Statement statement;
@Mock
private EntityManagerFactory entityManagerFactory;
private JpaTransactionManagerHandler jpaTransactionManagerHandler;
@Before
public void setUp() throws SQLException {
Connection connection = mock(Connection.class);
EntityManager entityManager = mock(EntityManager.class);
SessionImplementor sessionImplementor = mock(SessionImplementor.class);
when(connection.createStatement()).thenReturn(statement);
when(sessionImplementor.connection()).thenReturn(connection);
when(entityManager.unwrap(SessionImplementor.class)).thenReturn(sessionImplementor);
when(entityManagerFactory.createEntityManager()).thenReturn(entityManager);
when(transactionManager.getEntityManagerFactory()).thenReturn(entityManagerFactory);
jpaTransactionManagerHandler = new JpaTransactionManagerHandler(transactionManager);
}
@Test
public void assertSwitchTransactionTypeSuccess() throws SQLException {
jpaTransactionManagerHandler.switchTransactionType(TransactionType.XA);
verify(statement).execute(anyString());
TransactionSynchronizationManager.unbindResourceIfPossible(entityManagerFactory);
}
@Test(expected = ShardingException.class)
public void assertSwitchTransactionTypeFailExecute() throws SQLException {
when(statement.execute(anyString())).thenThrow(new SQLException("Mock send switch transaction type SQL failed"));
try {
jpaTransactionManagerHandler.switchTransactionType(TransactionType.XA);
} finally {
TransactionSynchronizationManager.unbindResourceIfPossible(entityManagerFactory);
}
}
@Test
public void assertUnbindResource() {
EntityManagerHolder holder = mock(EntityManagerHolder.class);
EntityManager entityManager = entityManagerFactory.createEntityManager();
when(holder.getEntityManager()).thenReturn(entityManager);
TransactionSynchronizationManager.bindResource(entityManagerFactory, holder);
jpaTransactionManagerHandler.unbindResource();
assertNull(TransactionSynchronizationManager.getResource(entityManagerFactory));
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.shardingsphere.transaction.util;
import org.apache.shardingsphere.transaction.aspect.ShardingTransactionalAspect;
import org.apache.shardingsphere.transaction.fixture.ShardingTransactionalTestService;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.hibernate.engine.spi.SessionImplementor;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TransactionManagerMockUtil {
/**
* Init transaction manager mock.
*
* @param statement mock statement
* @param jpaTransactionManager mock jpa transaction manager
* @param dataSourceTransactionManager mock dataSource transaction manager
* @throws SQLException SQL exception
*/
public static void initTransactionManagerMock(
final Statement statement, final JpaTransactionManager jpaTransactionManager, final DataSourceTransactionManager dataSourceTransactionManager) throws SQLException {
DataSource dataSource = mock(DataSource.class);
Connection connection = mock(Connection.class);
EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class);
EntityManager entityManager = mock(EntityManager.class);
SessionImplementor sessionImplementor = mock(SessionImplementor.class);
when(dataSource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
when(sessionImplementor.connection()).thenReturn(connection);
when(entityManager.unwrap(SessionImplementor.class)).thenReturn(sessionImplementor);
when(entityManagerFactory.createEntityManager()).thenReturn(entityManager);
when(jpaTransactionManager.getEntityManagerFactory()).thenReturn(entityManagerFactory);
when(dataSourceTransactionManager.getDataSource()).thenReturn(dataSource);
}
/**
* Test change proxy transaction type to LOCAL with specified transaction manager.
*
* @param testService sharding transaction test service
* @param aspect sharding transaction aspect
* @param transactionManager specified transaction manager
* @throws SQLException SQL exception
*/
public static void testChangeProxyTransactionTypeToLOCAL(
final ShardingTransactionalTestService testService, final ShardingTransactionalAspect aspect, final PlatformTransactionManager transactionManager) throws SQLException {
aspect.setTransactionManager(transactionManager);
aspect.setEnvironment(getProxyDataSource());
testService.testChangeTransactionTypeToLOCAL();
}
/**
* Test change proxy transaction type to XA with specified transaction manager.
*
* @param testService sharding transaction test service
* @param aspect sharding transaction aspect
* @param transactionManager specified transaction manager
* @throws SQLException SQL exception
*/
public static void testChangeProxyTransactionTypeToXA(
final ShardingTransactionalTestService testService, final ShardingTransactionalAspect aspect, final PlatformTransactionManager transactionManager) throws SQLException {
aspect.setTransactionManager(transactionManager);
aspect.setEnvironment(getProxyDataSource());
testService.testChangeTransactionTypeToXA();
}
/**
* Test change proxy transaction type to BASE with specified transaction manager.
*
* @param testService sharding transaction test service
* @param aspect sharding transaction aspect
* @param transactionManager specified transaction manager
* @throws SQLException SQL exception
*/
public static void testChangeProxyTransactionTypeToBASE(
final ShardingTransactionalTestService testService, final ShardingTransactionalAspect aspect, final PlatformTransactionManager transactionManager) throws SQLException {
aspect.setTransactionManager(transactionManager);
aspect.setEnvironment(getProxyDataSource());
testService.testChangeTransactionTypeToBASE();
}
private static DataSource[] getProxyDataSource() throws SQLException {
DataSource dataSource = mock(DataSource.class);
Connection connection = mock(Connection.class);
DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);
when(databaseMetaData.getDatabaseProductVersion()).thenReturn("5.6.0-Sharding-Proxy x.x.x");
when(connection.getMetaData()).thenReturn(databaseMetaData);
when(dataSource.getConnection()).thenReturn(connection);
return new DataSource[] {dataSource};
}
}
<?xml version="1.0"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%-5level] %d{HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.apache.shardingsphere" level="warn" additivity="false">
<appender-ref ref="console"/>
</logger>
<root>
<level value="error" />
<appender-ref ref="console" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF licenses this file to You under the Apache License, Version 2.0
~ (the "License"); you may not use this file except in compliance with
~ the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="META-INF/shardingTransaction.xml"/>
<bean id="testService" class="org.apache.shardingsphere.transaction.fixture.ShardingTransactionalTestService"/>
<bean id="drive" class="org.h2.Driver"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
<property name="driver" ref="drive"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册