SpringBoot + MyBatis + Oracle 实现事务配置管理分析

访客 阅读:240 2021-03-31 13:29:23 评论:0

SpringBoot 项目pom.xml 依赖配置文件:

<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> 
	<groupId>com.zzg</groupId> 
	<artifactId>migrate</artifactId> 
	<version>0.0.1-SNAPSHOT</version> 
 
	<parent> 
		<groupId>org.springframework.boot</groupId> 
		<artifactId>spring-boot-starter-parent</artifactId> 
		<version>1.5.18.RELEASE</version> 
	</parent> 
 
	<properties> 
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
		<encoding>UTF-8</encoding> 
		<java.version>1.8</java.version> 
		<commons-lang.version>2.6</commons-lang.version> 
		<commons-codec.version>1.10</commons-codec.version> 
		<commons-lang3.version>3.9</commons-lang3.version> 
		<commons-net.version>3.6</commons-net.version> 
		<commons-io.version>2.6</commons-io.version> 
		<commons-collections.version>3.2.1</commons-collections.version> 
		<commons-text.version>1.8</commons-text.version> 
		<oracle.version>1.0</oracle.version> 
		<mybatis-spring-boot-starter.version>1.3.2</mybatis-spring-boot-starter.version> 
		<com.alibaba.druid.version>1.1.10</com.alibaba.druid.version> 
	</properties> 
 
	<dependencies> 
		<!--web 模块依赖 --> 
		<dependency> 
			<groupId>org.springframework.boot</groupId> 
			<artifactId>spring-boot-starter-web</artifactId> 
		</dependency> 
		<!--web 单元测试 --> 
		<dependency> 
			<groupId>org.springframework.boot</groupId> 
			<artifactId>spring-boot-starter-test</artifactId> 
			<scope>test</scope> 
		</dependency> 
		<!-- apache common 模块 --> 
		<!--commons-lang3 工具包 --> 
		<dependency> 
			<groupId>org.apache.commons</groupId> 
			<artifactId>commons-lang3</artifactId> 
			<version>${commons-lang3.version}</version> 
		</dependency> 
		<!--commons-codec 加密工具包 --> 
		<dependency> 
			<groupId>commons-codec</groupId> 
			<artifactId>commons-codec</artifactId> 
			<version>${commons-codec.version}</version> 
		</dependency> 
		<!--commons-net 网络工具包 --> 
		<dependency> 
			<groupId>commons-net</groupId> 
			<artifactId>commons-net</artifactId> 
			<version>${commons-net.version}</version> 
		</dependency> 
		<!--common-io 工具包 --> 
		<dependency> 
			<groupId>commons-io</groupId> 
			<artifactId>commons-io</artifactId> 
			<version>${commons-io.version}</version> 
		</dependency> 
		<!--common-collection 工具包 --> 
		<dependency> 
			<groupId>commons-collections</groupId> 
			<artifactId>commons-collections</artifactId> 
			<version>${commons-collections.version}</version> 
		</dependency> 
		<!-- common-text 工具包 --> 
		<dependency> 
			<groupId>org.apache.commons</groupId> 
			<artifactId>commons-text</artifactId> 
			<version>${commons-text.version}</version> 
		</dependency> 
		<!-- springboot整合mybatis --> 
		<dependency> 
			<groupId>org.mybatis.spring.boot</groupId> 
			<artifactId>mybatis-spring-boot-starter</artifactId> 
			<version>${mybatis-spring-boot-starter.version}</version> 
		</dependency> 
		<!-- oracle驱动 --> 
		<dependency> 
 			<groupId>com.oracle</groupId> 
  			<artifactId>ojdbc6</artifactId> 
  			<version>${oracle.version}</version> 
		</dependency> 
		<!-- alibaba druid 数据库连接池  --> 
		<dependency> 
			<groupId>com.alibaba</groupId> 
			<artifactId>druid-spring-boot-starter</artifactId> 
			<version>${com.alibaba.druid.version}</version> 
		</dependency> 
		<!-- PageHelp  --> 
         <dependency> 
            <groupId>com.github.pagehelper</groupId> 
            <artifactId>pagehelper-spring-boot-starter</artifactId> 
            <version>1.2.5</version> 
        </dependency> 
        <!-- 集成Servlet-api 模块  --> 
        <dependency>  
		   <groupId>javax.servlet</groupId>  
		   <artifactId>servlet-api</artifactId>  
		   <version>2.5</version>  
		   <scope>provided</scope>  
		</dependency>  
	</dependencies> 
 
</project>

SpringBoot 项目结构截图:

步骤一:在application.properties配置文件中指定oracle 数据库连接用户、密码、数据库和MyBatis 映射的*mapper.xml 文件

说明:mybatis.mapper-locations 所指向的位置是从src/main/resource开始的,前边需要加上classpath,它指向的是你的mapper.xml文件放置的位置。

application.properties

# oracle 数据库连接配置 
spring.datasource.url=jdbc:oracle:thin:@192.168.1.150:1521:orcl 
spring.datasource.username=erms_zhx 
spring.datasource.password=erms_zhx 
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver 
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 
# mybatis 配置 
mybatis.mapper-locations=classpath*:out-mapper/*Mapper.xml

Spring Boot 如何完成Oracle 数据库初始化连接?

1、Spring Boot 项目启动时,实现DataSource及相关配置自动加载,[email protected] [email protected] 属性。

2、SpringBoot [email protected] 属性,会调用org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration配置类

  核心源码:

@Configuration 
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) 
@EnableConfigurationProperties(DataSourceProperties.class) 
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class }) 
public class DataSourceAutoConfiguration { 
    @Configuration 
	@Conditional(EmbeddedDatabaseCondition.class) 
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) 
	@Import(EmbeddedDataSourceConfiguration.class) 
	protected static class EmbeddedDatabaseConfiguration { 
 
	} 
 
    @Configuration 
	@Conditional(PooledDataSourceCondition.class) 
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) 
	@Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class, 
			DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class, 
			DataSourceConfiguration.Generic.class }) 
	@SuppressWarnings("deprecation") 
	protected static class PooledDataSourceConfiguration { 
 
	} 
}

说明:DataSourceAutoConfiguration包含两个嵌套类,一个是EmbeddedDatabaseConfiguration(内嵌数据库),另一个是PooledDataSourceConfiguration(数据库连接池)

EmbeddedDatabaseConfiguration表示已经嵌入Spring Boot的DataSource,仅仅需要在Maven中加入内嵌数据库的Driver,不做其他额外配置就能使用。从EmbeddedDatabaseType类可以看出,Spring Boot的内嵌DataSource支持HSQL,H2,DERBY这三种DB。

PooledDataSourceConfiguration表示Spring Boot还支持一些实现Pool的DataSource。从org.springframework.boot.jdbc.DataSourceBuilder中可以看出,当前版本的Spring Boot(2.0)只支持com.zaxxer.hikari.HikariDataSource,org.apache.tomcat.jdbc.pool.DataSource,org.apache.commons.dbcp2.BasicDataSource。其中,性能更加优秀的HikariDataSource是Spring Boot的默认选择。

3、SpringBoot 项目很少使用内嵌数据库进行数据存储,基于Spring Boot默认数据库连接池实现(HikariDataSource),继续讲解数据库连接池如何完成初始化。

继续调用PooledDataSourceConfigurationorg.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration抽象类的Hikari嵌套类(DataSourceConfiguration抽象类的一个实现类)。

核心源码:

abstract class DataSourceConfiguration { 
 
	@SuppressWarnings("unchecked") 
	protected static <T> T createDataSource(DataSourceProperties properties, 
			Class<? extends DataSource> type) { 
		return (T) properties.initializeDataSourceBuilder().type(type).build(); 
	} 
 
	/** 
	 * Tomcat Pool DataSource configuration. 
	 */ 
	@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) 
	@ConditionalOnMissingBean(DataSource.class) 
	@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true) 
	static class Tomcat { 
 
		@Bean 
		@ConfigurationProperties(prefix = "spring.datasource.tomcat") 
		public org.apache.tomcat.jdbc.pool.DataSource dataSource( 
				DataSourceProperties properties) { 
			org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource( 
					properties, org.apache.tomcat.jdbc.pool.DataSource.class); 
			DatabaseDriver databaseDriver = DatabaseDriver 
					.fromJdbcUrl(properties.determineUrl()); 
			String validationQuery = databaseDriver.getValidationQuery(); 
			if (validationQuery != null) { 
				dataSource.setTestOnBorrow(true); 
				dataSource.setValidationQuery(validationQuery); 
			} 
			return dataSource; 
		} 
 
	} 
 
	/** 
	 * Hikari DataSource configuration. 
	 */ 
	@ConditionalOnClass(HikariDataSource.class) 
	@ConditionalOnMissingBean(DataSource.class) 
	@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true) 
	static class Hikari { 
 
		@Bean 
		@ConfigurationProperties(prefix = "spring.datasource.hikari") 
		public HikariDataSource dataSource(DataSourceProperties properties) { 
			return createDataSource(properties, HikariDataSource.class); 
		} 
 
	} 
 
    ***省略其他代码片段*** 
}

说明:SpringBoot 默认使用HikariDataSource作为数据库连接池,HikariDataSource 对象实例化需要依赖两个配置对象:一个是org.springframework.boot.autoconfigure.jdbc.DataSourceProperties,另一个则是本类HikariDataSource的父类com.zaxxer.hikari.HikariConfig

4、Spring Boot实例化HikariDataSource对象后,会通过HikariDataSource.getConnection()方法创建HikariPool对象。HikariPool及其父类PoolBase做了许多复杂的工作,包括创建Pool,创建Connection,读取Config,验证等等。调用HikariDataSource.getConnection()方法最终得到了这个Connection对象。这个过程中主要做了以下几步:

① 创建HikariPool对象。

② 调用HikariPool对象的父类对象PoolBase的构造器,读取HikariConfig配置信息配置PoolBase的属性。

③ 调用PoolBase的构造器的initializeDataSource方法,利用com.zaxxer.hikari.util.DriverDataSource创建DataSource对象(这里主要指JDBC URL方式)。DriverDataSource中会把所有的DataSource信息封装到driverProperties属性中,这是为了适配java.sql.Driverconnect(String url, java.util.Properties info)方法。

核心源代码:

package com.zaxxer.hikari.util; 
 
import java.io.PrintWriter; 
import java.sql.Connection; 
import java.sql.Driver; 
import java.sql.DriverManager; 
import java.sql.SQLException; 
import java.sql.SQLFeatureNotSupportedException; 
import java.util.Enumeration; 
import java.util.Map.Entry; 
import java.util.Properties; 
 
import javax.sql.DataSource; 
 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
 
public final class DriverDataSource implements DataSource 
{ 
   private static final Logger LOGGER = LoggerFactory.getLogger(DriverDataSource.class); 
 
   private final String jdbcUrl; 
   private final Properties driverProperties; 
   private Driver driver; 
 
   public DriverDataSource(String jdbcUrl, String driverClassName, Properties properties, String username, String password) 
   { 
      this.jdbcUrl = jdbcUrl; 
      this.driverProperties = new Properties(); 
 
      for (Entry<Object, Object> entry : properties.entrySet()) { 
         driverProperties.setProperty(entry.getKey().toString(), entry.getValue().toString()); 
      } 
 
      if (username != null) { 
         driverProperties.put("user", driverProperties.getProperty("user", username)); 
      } 
      if (password != null) { 
         driverProperties.put("password", driverProperties.getProperty("password", password)); 
      } 
 
      if (driverClassName != null) { 
         Enumeration<Driver> drivers = DriverManager.getDrivers(); 
         while (drivers.hasMoreElements()) { 
            Driver d = drivers.nextElement(); 
            if (d.getClass().getName().equals(driverClassName)) { 
               driver = d; 
               break; 
            } 
         } 
 
         if (driver == null) { 
            LOGGER.warn("Registered driver with driverClassName={} was not found, trying direct instantiation.", driverClassName); 
            Class<?> driverClass = null; 
            ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader(); 
            try { 
               if (threadContextClassLoader != null) { 
                  try { 
                     driverClass = threadContextClassLoader.loadClass(driverClassName); 
                     LOGGER.debug("Driver class {} found in Thread context class loader {}", driverClassName, threadContextClassLoader); 
                  } 
                  catch (ClassNotFoundException e) { 
                     LOGGER.debug("Driver class {} not found in Thread context class loader {}, trying classloader {}", 
                                  driverClassName, threadContextClassLoader, this.getClass().getClassLoader()); 
                  } 
               } 
 
               if (driverClass == null) { 
                  driverClass = this.getClass().getClassLoader().loadClass(driverClassName); 
                  LOGGER.debug("Driver class {} found in the HikariConfig class classloader {}", driverClassName, this.getClass().getClassLoader()); 
               } 
            } catch (ClassNotFoundException e) { 
               LOGGER.debug("Failed to load driver class {} from HikariConfig class classloader {}", driverClassName, this.getClass().getClassLoader()); 
            } 
 
            if (driverClass != null) { 
               try { 
                  driver = (Driver) driverClass.newInstance(); 
               } catch (Exception e) { 
                  LOGGER.warn("Failed to create instance of driver class {}, trying jdbcUrl resolution", driverClassName, e); 
               } 
            } 
         } 
      } 
 
      final String sanitizedUrl = jdbcUrl.replaceAll("([?&;]password=)[^&#;]*(.*)", "$1<masked>$2"); 
      try { 
         if (driver == null) { 
            driver = DriverManager.getDriver(jdbcUrl); 
            LOGGER.debug("Loaded driver with class name {} for jdbcUrl={}", driver.getClass().getName(), sanitizedUrl); 
         } 
         else if (!driver.acceptsURL(jdbcUrl)) { 
            throw new RuntimeException("Driver " + driverClassName + " claims to not accept jdbcUrl, " + sanitizedUrl); 
         } 
      } 
      catch (SQLException e) { 
         throw new RuntimeException("Failed to get driver instance for jdbcUrl=" + sanitizedUrl, e); 
      } 
   } 
 
   @Override 
   public Connection getConnection() throws SQLException 
   { 
      return driver.connect(jdbcUrl, driverProperties); 
   } 
 
   @Override 
   public Connection getConnection(final String username, final String password) throws SQLException 
   { 
      final Properties cloned = (Properties) driverProperties.clone(); 
      if (username != null) { 
         cloned.put("user", username); 
         if (cloned.containsKey("username")) { 
            cloned.put("username", username); 
         } 
      } 
      if (password != null) { 
         cloned.put("password", password); 
      } 
 
      return driver.connect(jdbcUrl, cloned); 
   } 
 
   ***省略部分代码*** 
}

④ 调用HikariPool对象的构造器,同样也是配置一堆线程池信息。

⑤ 返回HikariPool.getConnection()。这个过程中,做了包含PoolBase.newPoolEntry()PoolBase.newConnection()的许多复杂方法。从PoolBase.newConnection()可以看出,最终还是调用的步骤③的getConnection()方法获取到了这个Connection对象。

abstract class PoolBase 
{ 
/***省略部分代码***/ 
/** 
    * Obtain connection from data source. 
    * 
    * @return a Connection connection 
    */ 
   private Connection newConnection() throws Exception 
   { 
      final long start = currentTime(); 
 
      Connection connection = null; 
      try { 
         String username = config.getUsername(); 
         String password = config.getPassword(); 
 
         connection = (username == null) ? dataSource.getConnection() : dataSource.getConnection(username, password); 
         if (connection == null) { 
            throw new SQLTransientConnectionException("DataSource returned null unexpectedly"); 
         } 
 
         setupConnection(connection); 
         lastConnectionFailure.set(null); 
         return connection; 
      } 
      catch (Exception e) { 
         if (connection != null) { 
            quietlyCloseConnection(connection, "(Failed to create/setup connection)"); 
         } 
         else if (getLastConnectionFailure() == null) { 
            LOGGER.debug("{} - Failed to create/setup connection: {}", poolName, e.getMessage()); 
         } 
 
         lastConnectionFailure.set(e); 
         throw e; 
      } 
      finally { 
         // tracker will be null during failFast check 
         if (metricsTracker != null) { 
            metricsTracker.recordConnectionCreated(elapsedMillis(start)); 
         } 
      } 
   } 
}

至此完成数据库连接初始化。

步骤二:[email protected]

说明:@MapperScan(basePackages = {"com.zzg.mapper"})    //扫描的是mapper.xml中namespace指向值的包位置

package com.zzg; 
 
import org.mybatis.spring.annotation.MapperScan; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.transaction.annotation.EnableTransactionManagement; 
 
@SpringBootApplication//扫描同级包及子包 
@EnableTransactionManagement //如果mybatis中service实现类中加入事务注解,需要此处添加该注解 
@MapperScan(basePackages = {"com.zzg.mapper"}) 
public class MainApplication { 
 
	public static void main(String[] args) { 
		// TODO Auto-generated method stub 
		SpringApplication.run(MainApplication.class, args); 
	} 
 
} 

步骤三:SpringBoot+MyBatis事务配置,等于在SpringBoot+MyBatis 配置基础上添加两个事务注解

事务配置依赖的注解:@EnableTransactionManagement [email protected] ,它们来源于spring-tx.jar 包

spring-tx.jar依赖于pom.xml 文件片段:

<!-- springboot整合mybatis --> 
		<dependency> 
			<groupId>org.mybatis.spring.boot</groupId> 
			<artifactId>mybatis-spring-boot-starter</artifactId> 
			<version>${mybatis-spring-boot-starter.version}</version> 
		</dependency>

找到service实现类,[email protected] 注解,如果你加在类上,那该类所有的方法都会被事务管理,如果你加在方法上,那仅仅是该方法被事务管理。事务管理一般是加载拥有增、删、改方法才会需要事务。

@Override 
	@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class) 
	public int insert(ErmsGuideProjBaseInfo record) { 
		// TODO Auto-generated method stub 
		return mapper.insert(record); 
	} 
 
	@Override 
	@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class) 
	public int insertSelective(ErmsGuideProjBaseInfo record) { 
		// TODO Auto-generated method stub 
		return mapper.insertSelective(record); 
	} 
 
	@Override 
	public ErmsGuideProjBaseInfo selectByPrimaryKey(Long sid) { 
		// TODO Auto-generated method stub 
		return mapper.selectByPrimaryKey(sid); 
	} 
 
	@Override 
	@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class) 
	public int updateByPrimaryKeySelective(ErmsGuideProjBaseInfo record) { 
		// TODO Auto-generated method stub 
		return mapper.updateByPrimaryKeySelective(record); 
	} 
 
	@Override 
	@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class) 
	public int updateByPrimaryKey(ErmsGuideProjBaseInfo record) { 
		// TODO Auto-generated method stub 
		return mapper.updateByPrimaryKey(record); 
	}

spring boot启动类必须要开启事务,[email protected]

package com.zzg; 
 
import org.mybatis.spring.annotation.MapperScan; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.transaction.annotation.EnableTransactionManagement; 
 
@SpringBootApplication//扫描同级包及子包 
@EnableTransactionManagement //如果mybatis中service实现类中加入事务注解,需要此处添加该注解 
@MapperScan(basePackages = {"com.zzg.mapper"}) 
public class MainApplication { 
 
	public static void main(String[] args) { 
		// TODO Auto-generated method stub 
		SpringApplication.run(MainApplication.class, args); 
	} 
 
}

效果展示:

声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
KIKK导航

KIKK导航

排行榜
关注我们

一个IT知识分享的公众号