diff --git a/ruoyi-admin/src/main/resources/application-mybatis.yml b/ruoyi-admin/src/main/resources/application-mybatis.yml index 4bb5bde..1cdb9f5 100644 --- a/ruoyi-admin/src/main/resources/application-mybatis.yml +++ b/ruoyi-admin/src/main/resources/application-mybatis.yml @@ -1,11 +1,11 @@ # MyBatis配置 -# mybatis: -# # 搜索指定包别名 -# typeAliasesPackage: com.ruoyi.**.domain -# # 配置mapper的扫描,找到所有的mapper.xml映射文件 -# mapperLocations: classpath*:mapper/**/*Mapper.xml -# # 加载全局的配置文件 -# configLocation: classpath:mybatis/mybatis-config.xml +mybatis: + # 搜索指定包别名 + typeAliasesPackage: com.ruoyi.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml index b092243..4d14a1a 100644 --- a/ruoyi-framework/pom.xml +++ b/ruoyi-framework/pom.xml @@ -40,6 +40,18 @@ sharding-jdbc-core + + com.atomikos + transactions-spring-boot3-starter + 6.0.0 + + + + javax.transaction + jta + 1.1 + + pro.fessional diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java index 1d4dc1f..c664c33 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java @@ -1,6 +1,7 @@ package com.ruoyi.framework.config; import java.util.TimeZone; + import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; @@ -16,7 +17,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy; // 表示通过aop框架暴露该代理对象,AopContext能够访问 @EnableAspectJAutoProxy(exposeProxy = true) // 指定要扫描的Mapper类的包的路径 -@MapperScan("com.ruoyi.**.mapper") +@MapperScan(basePackages = "com.ruoyi.**.mapper", sqlSessionTemplateRef = "sqlSessionTemplate") public class ApplicationConfig { /** diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosConfig.java new file mode 100644 index 0000000..b7d3dcc --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/AtomikosConfig.java @@ -0,0 +1,47 @@ +package com.ruoyi.framework.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.jta.JtaTransactionManager; + +import com.atomikos.icatch.jta.UserTransactionImp; +import com.atomikos.icatch.jta.UserTransactionManager; + +import jakarta.transaction.TransactionManager; +import jakarta.transaction.UserTransaction; + +/** + * JTA 事务配置 + * + * @author ruoyi + */ +@Configuration +public class AtomikosConfig +{ + @Bean(name = "userTransaction") + public UserTransaction userTransaction() throws Throwable + { + UserTransactionImp userTransactionImp = new UserTransactionImp(); + userTransactionImp.setTransactionTimeout(10000); + return userTransactionImp; + } + + @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close") + public TransactionManager atomikosTransactionManager() throws Throwable + { + UserTransactionManager userTransactionManager = new UserTransactionManager(); + userTransactionManager.setForceShutdown(false); + return userTransactionManager; + } + + @Bean(name = "transactionManager") + @DependsOn({ "userTransaction", "atomikosTransactionManager" }) + public PlatformTransactionManager transactionManager() throws Throwable + { + UserTransaction userTransaction = userTransaction(); + TransactionManager atomikosTransactionManager = atomikosTransactionManager(); + return new JtaTransactionManager(userTransaction, atomikosTransactionManager); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java index 18a0f4b..b58c17e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -3,22 +3,25 @@ package com.ruoyi.framework.config; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; -import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder; import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.util.Utils; +import com.atomikos.jdbc.AtomikosDataSourceBean; import com.ruoyi.common.enums.DataSourceType; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.config.properties.DruidProperties; @@ -40,27 +43,69 @@ public class DruidConfig { Logger logger = LoggerFactory.getLogger(DruidConfig.class); + public static final String MASTER = DataSourceType.MASTER.name(); + + public static final String SLAVE = DataSourceType.SLAVE.name(); + + @Autowired + private DruidProperties druidProperties; + @Bean + @Primary + @DependsOn({ "transactionManager" }) @ConfigurationProperties("spring.datasource.druid.master") - public DataSource masterDataSource(DruidProperties druidProperties) { - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); - return druidProperties.dataSource(dataSource); + public DataSource masterDataSource(Environment env) { + String prefix = "spring.datasource.druid.master."; + return getDataSource(env, prefix, MASTER); } @Bean @ConfigurationProperties("spring.datasource.druid.slave") + @DependsOn({ "transactionManager" }) @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") - public DataSource slaveDataSource(DruidProperties druidProperties) { - DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); - return druidProperties.dataSource(dataSource); + public DataSource slaveDataSource(Environment env) { + String prefix = "spring.datasource.druid.slave."; + return getDataSource(env, prefix, SLAVE); + } + + protected DataSource getDataSource(Environment env, String prefix, String dataSourceName) { + Properties prop = build(env, prefix); + AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); + ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource"); + // 添加连接池限制 + ds.setMaxPoolSize(50); + ds.setMinPoolSize(5); + ds.setBorrowConnectionTimeout(60); + ds.setUniqueResourceName(dataSourceName); + ds.setXaProperties(prop); + return ds; + } + + protected Properties build(Environment env, String prefix) { + Properties prop = new Properties(); + prop.put("url", env.getProperty(prefix + "url")); + prop.put("username", env.getProperty(prefix + "username")); + prop.put("password", env.getProperty(prefix + "password")); + prop.put("initialSize", druidProperties.getInitialSize()); + prop.put("minIdle", druidProperties.getMinIdle()); + prop.put("maxActive", druidProperties.getMaxActive()); + prop.put("maxWait", druidProperties.getMaxWait()); + prop.put("timeBetweenEvictionRunsMillis", druidProperties.getTimeBetweenEvictionRunsMillis()); + prop.put("minEvictableIdleTimeMillis", druidProperties.getMinEvictableIdleTimeMillis()); + prop.put("maxEvictableIdleTimeMillis", druidProperties.getMaxEvictableIdleTimeMillis()); + prop.put("validationQuery", druidProperties.getValidationQuery()); + prop.put("testWhileIdle", druidProperties.isTestWhileIdle()); + prop.put("testOnBorrow", druidProperties.isTestOnBorrow()); + prop.put("testOnReturn", druidProperties.isTestOnReturn()); + return prop; } @Bean(name = "dynamicDataSource") @Primary public DynamicDataSource dataSource(DataSource masterDataSource) { Map targetDataSources = new HashMap<>(); - targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); - setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + targetDataSources.put(MASTER, masterDataSource); + setDataSource(targetDataSources, SLAVE, "slaveDataSource"); setDataSource(targetDataSources, DataSourceType.SHARDING.name(), "shardingDataSource"); return new DynamicDataSource(masterDataSource, targetDataSources); } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java index 6a49b9a..ec3bad0 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java @@ -3,8 +3,10 @@ package com.ruoyi.framework.config; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import javax.sql.DataSource; @@ -12,10 +14,13 @@ import org.apache.ibatis.io.VFS; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.core.env.Environment; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; @@ -26,7 +31,10 @@ import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.util.ClassUtils; +import com.ruoyi.common.enums.DataSourceType; import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate; /** * Mybatis支持*匹配扫描包 @@ -34,14 +42,12 @@ import com.ruoyi.common.utils.StringUtils; * @author ruoyi */ @Configuration -@ConditionalOnProperty(prefix = "mybatis", name = { "typeAliasesPackage", "mapperLocations", - "configLocation" }, matchIfMissing = false) public class MyBatisConfig { - @Autowired - private Environment env; static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; + Logger logger = LoggerFactory.getLogger(MyBatisConfig.class); + public static String setTypeAliasesPackage(String typeAliasesPackage) { ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); @@ -100,8 +106,7 @@ public class MyBatisConfig { return resources.toArray(new Resource[resources.size()]); } - @Bean - public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { + public SqlSessionFactory createSqlSessionFactory(Environment env, DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatis.mapperLocations"); String configLocation = env.getProperty("mybatis.configLocation"); @@ -115,4 +120,39 @@ public class MyBatisConfig { sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); return sessionFactory.getObject(); } + + @Bean(name = "sqlSessionFactoryMaster") + @Primary + public SqlSessionFactory sqlSessionFactoryMaster(Environment env, + @Qualifier("masterDataSource") DataSource dataSource) throws Exception { + return createSqlSessionFactory(env, dataSource); + } + + @Bean(name = "sqlSessionFactorySlave") + @ConditionalOnBean(name = "slaveDataSource") + public SqlSessionFactory sqlSessionFactorySlave(Environment env, + @Qualifier("slaveDataSource") DataSource dataSource) throws Exception { + return createSqlSessionFactory(env, dataSource); + } + + @Bean(name = "sqlSessionTemplate") + public DynamicSqlSessionTemplate sqlSessionTemplate( + @Qualifier("sqlSessionFactoryMaster") SqlSessionFactory factoryMaster) throws Exception { + Map sqlSessionFactoryMap = new HashMap<>(); + sqlSessionFactoryMap.put(DruidConfig.MASTER, factoryMaster); + putSqlSessionFactory("sqlSessionFactorySlave", DataSourceType.SLAVE, sqlSessionFactoryMap); + DynamicSqlSessionTemplate customSqlSessionTemplate = new DynamicSqlSessionTemplate(factoryMaster); + customSqlSessionTemplate.setTargetSqlSessionFactorys(sqlSessionFactoryMap); + return customSqlSessionTemplate; + } + + private void putSqlSessionFactory(String sqlSessionFactoryName, DataSourceType dataSourceType, + Map sqlSessionFactoryMap) { + try { + SqlSessionFactory factorySlave = SpringUtils.getBean(sqlSessionFactoryName); + sqlSessionFactoryMap.put(dataSourceType, factorySlave); + } catch (Exception e) { + logger.error("Failed to register a SqlSessionFactory:{}", sqlSessionFactoryName); + } + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java index c8a5c8a..a07f066 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java @@ -2,6 +2,7 @@ package com.ruoyi.framework.config.properties; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; + import com.alibaba.druid.pool.DruidDataSource; /** @@ -10,8 +11,7 @@ import com.alibaba.druid.pool.DruidDataSource; * @author ruoyi */ @Configuration -public class DruidProperties -{ +public class DruidProperties { @Value("${spring.datasource.druid.initialSize}") private int initialSize; @@ -51,8 +51,7 @@ public class DruidProperties @Value("${spring.datasource.druid.testOnReturn}") private boolean testOnReturn; - public DruidDataSource dataSource(DruidDataSource datasource) - { + public DruidDataSource dataSource(DruidDataSource datasource) { /** 配置初始化大小、最小、最大 */ datasource.setInitialSize(initialSize); datasource.setMaxActive(maxActive); @@ -60,10 +59,10 @@ public class DruidProperties /** 配置获取连接等待超时的时间 */ datasource.setMaxWait(maxWait); - + /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ datasource.setConnectTimeout(connectTimeout); - + /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ datasource.setSocketTimeout(socketTimeout); @@ -75,10 +74,13 @@ public class DruidProperties datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); /** - * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select + * 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 */ datasource.setValidationQuery(validationQuery); - /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + /** + * 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 + */ datasource.setTestWhileIdle(testWhileIdle); /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ datasource.setTestOnBorrow(testOnBorrow); @@ -86,4 +88,108 @@ public class DruidProperties datasource.setTestOnReturn(testOnReturn); return datasource; } + + public int getInitialSize() { + return initialSize; + } + + public void setInitialSize(int initialSize) { + this.initialSize = initialSize; + } + + public int getMinIdle() { + return minIdle; + } + + public void setMinIdle(int minIdle) { + this.minIdle = minIdle; + } + + public int getMaxActive() { + return maxActive; + } + + public void setMaxActive(int maxActive) { + this.maxActive = maxActive; + } + + public int getMaxWait() { + return maxWait; + } + + public void setMaxWait(int maxWait) { + this.maxWait = maxWait; + } + + public int getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public int getSocketTimeout() { + return socketTimeout; + } + + public void setSocketTimeout(int socketTimeout) { + this.socketTimeout = socketTimeout; + } + + public int getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + + public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + } + + public int getMinEvictableIdleTimeMillis() { + return minEvictableIdleTimeMillis; + } + + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) { + this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; + } + + public int getMaxEvictableIdleTimeMillis() { + return maxEvictableIdleTimeMillis; + } + + public void setMaxEvictableIdleTimeMillis(int maxEvictableIdleTimeMillis) { + this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis; + } + + public String getValidationQuery() { + return validationQuery; + } + + public void setValidationQuery(String validationQuery) { + this.validationQuery = validationQuery; + } + + public boolean isTestWhileIdle() { + return testWhileIdle; + } + + public void setTestWhileIdle(boolean testWhileIdle) { + this.testWhileIdle = testWhileIdle; + } + + public boolean isTestOnBorrow() { + return testOnBorrow; + } + + public void setTestOnBorrow(boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + + public boolean isTestOnReturn() { + return testOnReturn; + } + + public void setTestOnReturn(boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicSqlSessionTemplate.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicSqlSessionTemplate.java new file mode 100644 index 0000000..814d9d8 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicSqlSessionTemplate.java @@ -0,0 +1,358 @@ +package com.ruoyi.framework.datasource; + +import static java.lang.reflect.Proxy.*; +import static org.apache.ibatis.reflection.ExceptionUtil.*; +import static org.mybatis.spring.SqlSessionUtils.*; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.util.List; +import java.util.Map; + +import org.apache.ibatis.exceptions.PersistenceException; +import org.apache.ibatis.executor.BatchResult; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.MyBatisExceptionTranslator; +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.dao.support.PersistenceExceptionTranslator; + +/** + * 自定义SqlSessionTemplate,动态切换数据源 + * + * @author ruoyi + */ +public class DynamicSqlSessionTemplate extends SqlSessionTemplate +{ + private final SqlSessionFactory sqlSessionFactory; + private final ExecutorType executorType; + private final SqlSession sqlSessionProxy; + private final PersistenceExceptionTranslator exceptionTranslator; + private Map targetSqlSessionFactorys; + private SqlSessionFactory defaultTargetSqlSessionFactory; + + public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) + { + this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); + } + + public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) + { + this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator( + sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true)); + } + + public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, + PersistenceExceptionTranslator exceptionTranslator) + { + super(sqlSessionFactory, executorType, exceptionTranslator); + this.sqlSessionFactory = sqlSessionFactory; + this.executorType = executorType; + this.exceptionTranslator = exceptionTranslator; + this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), + new Class[] { SqlSession.class }, new SqlSessionInterceptor()); + this.defaultTargetSqlSessionFactory = sqlSessionFactory; + } + + public void setTargetSqlSessionFactorys(Map targetSqlSessionFactorys) + { + this.targetSqlSessionFactorys = targetSqlSessionFactorys; + } + + public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) + { + this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory; + } + + @Override + public SqlSessionFactory getSqlSessionFactory() + { + SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys + .get(DynamicDataSourceContextHolder.getDataSourceType()); + if (targetSqlSessionFactory != null) + { + return targetSqlSessionFactory; + } + else if (defaultTargetSqlSessionFactory != null) + { + return defaultTargetSqlSessionFactory; + } + return this.sqlSessionFactory; + } + + @Override + public Configuration getConfiguration() + { + return this.getSqlSessionFactory().getConfiguration(); + } + + public ExecutorType getExecutorType() + { + return this.executorType; + } + + public PersistenceExceptionTranslator getPersistenceExceptionTranslator() + { + return this.exceptionTranslator; + } + + /** + * {@inheritDoc} + */ + public T selectOne(String statement) + { + return this.sqlSessionProxy. selectOne(statement); + } + + /** + * {@inheritDoc} + */ + public T selectOne(String statement, Object parameter) + { + return this.sqlSessionProxy. selectOne(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public Map selectMap(String statement, String mapKey) + { + return this.sqlSessionProxy. selectMap(statement, mapKey); + } + + /** + * {@inheritDoc} + */ + public Map selectMap(String statement, Object parameter, String mapKey) + { + return this.sqlSessionProxy. selectMap(statement, parameter, mapKey); + } + + /** + * {@inheritDoc} + */ + public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) + { + return this.sqlSessionProxy. selectMap(statement, parameter, mapKey, rowBounds); + } + + /** + * {@inheritDoc} + */ + public List selectList(String statement) + { + return this.sqlSessionProxy. selectList(statement); + } + + /** + * {@inheritDoc} + */ + public List selectList(String statement, Object parameter) + { + return this.sqlSessionProxy. selectList(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public List selectList(String statement, Object parameter, RowBounds rowBounds) + { + return this.sqlSessionProxy. selectList(statement, parameter, rowBounds); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("rawtypes") + public void select(String statement, ResultHandler handler) + { + this.sqlSessionProxy.select(statement, handler); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("rawtypes") + public void select(String statement, Object parameter, ResultHandler handler) + { + this.sqlSessionProxy.select(statement, parameter, handler); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("rawtypes") + public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) + { + this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); + } + + /** + * {@inheritDoc} + */ + public int insert(String statement) + { + return this.sqlSessionProxy.insert(statement); + } + + /** + * {@inheritDoc} + */ + public int insert(String statement, Object parameter) + { + return this.sqlSessionProxy.insert(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public int update(String statement) + { + return this.sqlSessionProxy.update(statement); + } + + /** + * {@inheritDoc} + */ + public int update(String statement, Object parameter) + { + return this.sqlSessionProxy.update(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public int delete(String statement) + { + return this.sqlSessionProxy.delete(statement); + } + + /** + * {@inheritDoc} + */ + public int delete(String statement, Object parameter) + { + return this.sqlSessionProxy.delete(statement, parameter); + } + + /** + * {@inheritDoc} + */ + public T getMapper(Class type) + { + return getConfiguration().getMapper(type, this); + } + + /** + * {@inheritDoc} + */ + public void commit() + { + throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void commit(boolean force) + { + throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void rollback() + { + throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void rollback(boolean force) + { + throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void close() + { + throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); + } + + /** + * {@inheritDoc} + */ + public void clearCache() + { + this.sqlSessionProxy.clearCache(); + } + + /** + * {@inheritDoc} + */ + public Connection getConnection() + { + return this.sqlSessionProxy.getConnection(); + } + + /** + * {@inheritDoc} + * + * @since 1.0.2 + */ + public List flushStatements() + { + return this.sqlSessionProxy.flushStatements(); + } + + /** + * Proxy needed to route MyBatis method calls to the proper SqlSession got from Spring's Transaction Manager It also + * unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to pass a {@code PersistenceException} to + * the {@code PersistenceExceptionTranslator}. + */ + private class SqlSessionInterceptor implements InvocationHandler + { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + final SqlSession sqlSession = getSqlSession(DynamicSqlSessionTemplate.this.getSqlSessionFactory(), + DynamicSqlSessionTemplate.this.executorType, DynamicSqlSessionTemplate.this.exceptionTranslator); + try + { + Object result = method.invoke(sqlSession, args); + if (!isSqlSessionTransactional(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory())) + { + sqlSession.commit(true); + } + return result; + } + catch (Throwable t) + { + Throwable unwrapped = unwrapThrowable(t); + if (DynamicSqlSessionTemplate.this.exceptionTranslator != null + && unwrapped instanceof PersistenceException) + { + Throwable translated = DynamicSqlSessionTemplate.this.exceptionTranslator + .translateExceptionIfPossible((PersistenceException) unwrapped); + if (translated != null) + { + unwrapped = translated; + } + } + throw unwrapped; + } + finally + { + closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory()); + } + } + } +} \ No newline at end of file