Spring 和 Mybaits整合

负责将代理类记性扫描,扫描的是Mapper接口所在的包,这个是mybatis提供的,所以会去找SqlSessionFactory
mybaits和 Spring整合的官网:http://mybatis.org/spring/zh/index.html

这个jar包是mybaits提供的。


要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。
在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean来创建 SqlSessionFactory。 要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

还需要 Spring 对ORM框架支持的jar包
|
<!-- Spring orm Spring提供spring-orm提供orm框架相关的支持。支持Hibernate、iBatis和JPA等 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.20.RELEASE</version> </dependency> |
|
<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.hy</groupId> <artifactId>ssm01</artifactId> <version>0.0.1</version> <packaging>war</packaging>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> </properties>
<dependencies> <!-- Spring core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!-- Spring orm Spring提供spring-orm提供orm框架相关的支持。支持Hibernate、iBatis和JPA等 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!-- spring-aspects会帮我们传递过来aspectjweaver --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!-- mybaits相关jar包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency>
<!-- mybaits和Spring整合包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
<!-- 数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.9</version> </dependency>
<!-- 连接数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>
<!-- Spring-Test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.20.RELEASE</version> </dependency>
<!-- Junit测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
<!-- logback日志 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
<!-- Mybatis EHCache整合包 --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </dependency> </dependencies>
<build> <plugins> <!-- 指定jdk,防止update project --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <!-- 项目编码 --> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project> |


|
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="true"> <!-- 指定日志输出的位置 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 日志输出的格式 --> <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 --> <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern> </encoder> </appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:TRACE > DEBUG > INFO > WARN > ERROR > FATAL --> <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 --> <root level="INFO"> <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender --> <appender-ref ref="STDOUT" /> </root>
<!-- 根据特殊需求指定局部日志级别 --> <logger name="com.hy.mapper" level="DEBUG" /> <logger name="com.hy.test" level="DEBUG" /> </configuration> |



|
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 加载外部属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 --> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.dev.driver}"/> <property name="url" value="${jdbc.dev.url}"/> <property name="username" value="${jdbc.dev.username}"/> <property name="password" value="${jdbc.dev.password}"/> </bean> </beans> |
|
package com.hy.test;
import java.sql.Connection; import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-mybatis.xml") public class Test01 { @Autowired private DataSource dataSource;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test public void testConnection() throws SQLException { Connection connection = dataSource.getConnection();
logger.debug(connection.toString()); } }
|

SqlSessionFactoryBean 是由MyBatis提供的package org.mybatis.spring;

SqlSessionFactoryBean 实现了 FactoryBean这个接口,

这个接口是由Spring提供的。

会调用 getObject方法得到一个 对象,这个对象是
类型的对象。

1)风格1:保留Mybaits全局配置文件(核心配置文件mybatis-config.xml)
|
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 数据库中的字段的_规则,转类中属性的驼峰标示写法 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration> |


|
<!-- 配置SqlSessionFactoryBean 创建的是 sqlSessionFactory,但是通过sqlSessionFactory 工厂类的对象给你 SqlSession对象--> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 装配数据源 --> <property name="dataSource" ref="druidDataSource"/>
<!-- 指定Mapper 映射文件的位置 --> <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
<!-- 指定 MyBatis 全局配置文件位置 --> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> |
配置 Mapper接口类型的bean扫描器
|
<!-- 配置 Mapper接口类型的bean扫描器 --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.hy.mapper"/> </bean> |


|
package com.hy.bean;
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString;
@NoArgsConstructor @AllArgsConstructor @Setter @Getter @ToString public class Emp { private Long empId; //用包装类有null值 private String empName; private String empPwd; private String empGender; private Double empSalary; //用包装类有null值
//构造方法(去ID的) public Emp(String empName, String empPwd, String empGender, Double empSalary) { super(); this.empName = empName; this.empPwd = empPwd; this.empGender = empGender; this.empSalary = empSalary; } } |
|
package com.hy.mapper;
import java.util.List; import java.util.Map;
import com.hy.bean.Emp;
public interface EmpMapper { abstract public Emp selectById(long empId);
abstract public int insert(Emp emp);
abstract public int deleteById(long empId);
abstract public int update(Emp emp);
abstract public int updateByMap(Map<String, Object> paramMap);
abstract public Integer selectCount();
abstract public Map<String, Object> selectForMap(int empId);
abstract public List<Emp> selectAll();
abstract public int insertWithKey(Emp emp); } |
|
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hy.mapper.EmpMapper"> <select id="selectById" resultType="com.hy.bean.Emp"> select emp_id empId,emp_name empName,emp_pwd empPwd,emp_gender empGender , emp_salary empSalary from sys_emp where emp_id = #{empId} </select> </mapper> |
|
package com.hy.test;
import java.sql.SQLException;
import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.hy.bean.Emp; import com.hy.mapper.EmpMapper;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-mybatis.xml") public class Test02 { @Autowired private EmpMapper empMapper;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test public void testEmpMapper () throws SQLException { Emp emp = empMapper.selectById(1);
logger.debug(emp.toString()); } } |

2)风格2:彻底舍弃Mybaits全局配置文件(核心配置文件mybatis-config.xml),所有的一切在spring的配置文件中配。
|
<!-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 指定 MyBatis 全局配置文件位置 --> <!-- <property name="configLocation" value="classpath:mybatis-config.xml"/> -->
<!-- 舍弃mybatis-config全局配置文件,使用configuration属性 --> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="mapUnderscoreToCamelCase" value="true"/> </bean> </property>
<!-- 舍弃mybatis-config全局配置文件,使用typeAliasesPackage属性,配置实体bean的别名 --> <property name="typeAliasesPackage" value="com.hy.bean"/>
<!-- 指定Mapper 映射文件的位置 --> <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
<!-- 装配数据源 --> <property name="dataSource" ref="druidDataSource"/> </bean> |


spring-mybaits.xml只扫描Service,
而Mapper是用mybatis自带的扫描器MapperScannerConfigurer扫描
|
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 在spring-mybatis中 扫描@Servie注解标识的组件 --> <context:component-scan base-package="com.hy.service"/>
<!-- 加载外部属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 --> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.dev.driver}"/> <property name="url" value="${jdbc.dev.url}"/> <property name="username" value="${jdbc.dev.username}"/> <property name="password" value="${jdbc.dev.password}"/> </bean>
<!-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 指定 MyBatis 全局配置文件位置 --> <property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 舍弃mybatis-config全局配置文件,使用configuration属性 <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="mapUnderscoreToCamelCase" value="true"/> </bean> </property> --> <!-- 舍弃mybatis-config全局配置文件,使用typeAliasesPackage属性,配置实体bean的别名 <property name="typeAliasesPackage" value="com.hy.bean"/> -->
<!-- 指定Mapper 映射文件的位置 --> <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
<!-- 装配数据源 --> <property name="dataSource" ref="druidDataSource"/> </bean>
<!-- 配置 Mapper接口类型的bean扫描器 --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.hy.mapper"/> </bean> </beans> |
|
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 数据库中的字段的_规则,转类中属性的驼峰标示写法 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration> |
事务功能的相关操作全部通过自己编写代码来实现:
|
Connection conn = ...;
try { // 开启事务:关闭事务的自动提交 conn.setAutoCommit(false);
// 核心操作
// 提交事务 conn.commit(); }catch(Exception e){ // 回滚事务 conn.rollBack(); }finally{ // 释放数据库连接 conn.close(); } |
编程式的实现方式存在缺陷:
1)具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐。
2)代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用。
既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。
封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作。
好处1:提高开发效率
好处2:消除了冗余的代码
好处3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方面的优化
编程式事务:自己写代码实现功能
声明式事务:通过配置让框架实现功能

PlatformTransactionManager 接口本身没有变化,它继承了 TransactionManager。TransactionManager接口中什么都没有,它的存在的意义是定义一个技术体系。


我们现在要使用的事务管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager,将来整合 Mybatis 用的也是这个类。
doBegin():开启事务
doCommit():提交事务
doRollback():回滚事务
doSuspend():挂起事务
doResume():恢复挂起的事务
|
package com.hy.service;
import com.hy.bean.Emp; public interface EmpService { abstract public Emp listById(long empId); } |
|
@Service public class EmpServiceImpl implements EmpService{ @Autowired private EmpMapper empMapper;
public Emp listById(long empId) { Emp emp = empMapper.selectById(empId); return emp; } } |

|
package com.hy.test;
import java.sql.SQLException;
import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.hy.bean.Emp; import com.hy.service.EmpService;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-mybatis.xml") public class Test03 { @Autowired private EmpService empService; private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test public void testEmpService() throws SQLException { Emp emp = empService.listById(1);
logger.debug(emp.toString()); } } |

|
package com.hy.mapper;
import java.util.List; import java.util.Map;
import org.apache.ibatis.annotations.Param;
import com.hy.bean.Emp;
public interface EmpMapper { abstract public void updateEmpNameById(@Param("empId") long empId, @Param("empName")String empName);
abstract public void updateEmpSalaryById(@Param("empId") long empId, @Param("empSalary")Double empSalary);
abstract public Emp selectById(long empId); } |
|
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hy.mapper.EmpMapper"> <select id="selectById" resultType="com.hy.bean.Emp"> select emp_id empId,emp_name empName,emp_pwd empPwd,emp_gender empGender , emp_salary empSalary from sys_emp where emp_id = #{empId} </select>
<!-- abstract public void updateEmpNameById(@Param("empId")long empId, @Param("empName")String empName); abstract public void updateEmpSalaryById(@Param("empId")long empId, @Param("empSalary")Double empsalary); --> <update id="updateEmpNameById"> update sys_emp set emp_name = #{empName} where emp_id = #{empId} </update>
<update id="updateEmpSalaryById"> update sys_emp set emp_salary = #{empSalary} where emp_id = #{empId} </update> </mapper>
|
|
package com.hy.service; import com.hy.bean.Emp; public interface EmpService { abstract public Emp listById(long empId);
abstract public int eidtEmp(Emp emp); } |
在editEmp方法中我们会根据empId,两次修改sys_emp表列的值
|
package com.hy.service.impl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
import com.hy.bean.Emp; import com.hy.mapper.EmpMapper; import com.hy.service.EmpService;
@Service public class EmpServiceImpl implements EmpService{ @Autowired private EmpMapper empMapper;
public Emp listById(long empId) { Emp emp = empMapper.selectById(empId); return emp; }
@Override public int eidtEmp(Emp emp) { empMapper.updateEmpNameById(emp.getEmpId(), emp.getEmpName()); empMapper.updateEmpSalaryById(emp.getEmpId(), emp.getEmpSalary()); return 0; } }
|
|
package com.hy.test;
import java.sql.SQLException;
import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.hy.bean.Emp; import com.hy.service.EmpService;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-mybatis.xml") public class Test03 { @Autowired private EmpService empService;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Test public void testEmpService() throws SQLException { Emp emp = empService.listById(1);
logger.debug(emp.toString()); }
@Test public void testEmpService2() throws SQLException { Emp emp = new Emp(1L, "范冰冰plus3", "fbbplus", "f", 1315d);
empService.eidtEmp(emp); } } |
出现异常的情况

事务功能的相关操作全部通过自己编写代码来实现:
|
Connection conn = ...;
try { // 开启事务:关闭事务的自动提交 conn.setAutoCommit(false);
// 核心操作
// 提交事务 conn.commit(); }catch(Exception e){ // 回滚事务 conn.rollBack(); }finally{ // 释放数据库连接 conn.close(); } |
编程式的实现方式存在缺陷:
1)具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐。
2) 代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用。
既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。
封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作。
好处1:提高开发效率
好处2:消除了冗余的代码
好处3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方面的优化
编程式事务:自己写代码实现功能
声明式事务:通过配置让框架实现功能

PlatformTransactionManager 接口本身没有变化,它继承了 TransactionManager。TransactionManager接口中什么都没有,它的存在的意义是定义一个技术体系。


我们现在要使用的事务管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager,将来整合 Mybatis 用的也是这个类。
doBegin():开启事务
doCommit():提交事务
doRollback():回滚事务
doSuspend():挂起事务
doResume():恢复挂起的事务
事务的挂起和恢复,主要是事务传播行为所体现的。
事务通常都是加到业务逻辑层,针对XxxService类使用事务
|
<!-- Spring orm Spring提供spring-orm提供orm框架相关的支持。支持Hibernate、iBatis和JPA等 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.2.20.RELEASE</version> </dependency>
|
Spring 持久化层支持的jar包,Spring在执行持久化操作与持久化技术进行整合过程中,需要使用orm,tx,jdbc三个jar包
导入orm包就可以通过maven的依赖传递把其他两个也导入进来。

|
<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 给事务管理器bean装配数据源,其他属性保持默认即可 --> <property name="dataSource" ref="druidDataSource"/> </bean> |
|
<!-- 开启基于注解的声明式事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> |

给XxxServiceImpl类的方法中加上 @Transactional 注解,Spring会自动的给这个方法加上事务。

|
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 在spring-mybatis中 扫描@Servie注解标识的组件 --> <context:component-scan base-package="com.hy.service"/>
<!-- 加载外部属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 --> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.dev.driver}"/> <property name="url" value="${jdbc.dev.url}"/> <property name="username" value="${jdbc.dev.username}"/> <property name="password" value="${jdbc.dev.password}"/> </bean>
<!-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 指定 MyBatis 全局配置文件位置 --> <property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 舍弃mybatis-config全局配置文件,使用configuration属性 <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="mapUnderscoreToCamelCase" value="true"/> </bean> </property> --> <!-- 舍弃mybatis-config全局配置文件,使用typeAliasesPackage属性,配置实体bean的别名 <property name="typeAliasesPackage" value="com.hy.bean"/> -->
<!-- 指定Mapper 映射文件的位置 --> <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
<!-- 装配数据源 --> <property name="dataSource" ref="druidDataSource"/> </bean>
<!-- 配置 Mapper接口类型的bean扫描器 --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.hy.mapper"/> </bean>
<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 装配数据源 --> <property name="dataSource" ref="druidDataSource"/> </bean>
<!-- 开启基于注解的声明式事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans> |
|
<!-- 根据特殊需求指定局部日志级别 --> <logger name="com.hy.mapper" level="DEBUG" /> <logger name="com.hy.test" level="DEBUG" /> <logger name="org.springframework.jdbc.datasource.DataSourceTransactionManager" level="DEBUG" /> |







|
package com.hy.service;
public interface EmpService { abstract public Emp listById(long empId); } |
|
package com.hy.service.impl;
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;
import com.hy.mapper.EmpMapper; import com.hy.service.EmpService;
@Service public class EmpServiceImpl implements EmpService { @Autowired private EmpMapper empMapper;
@Transactional @Override public Emp listById(long empId) { Emp emp = empMapper.selectById(empId); return emp; } } |
获取数据库链接
切换数据库链接为手动提交
提交事务
释放链接
[org.springframework.jdbc.datasource.DataSourceTransactionManager] [Creating new transaction with name [com.hy.service.impl.EmpServiceImpl.listById]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly]

这里为了现实效果,所以为查询配置了事务。
1) 事务的只读属性
2) 事务的超时属性
3) 回滚和不回滚的异常
4) 事务的隔离级别
5) 事务传播行为
对一个查询操作来说,如果我们把它设置成只读,就能明确告诉数据库,这个操作不涉及写(添加,删除,修改)操作,这样数据库就能够针对查询操作来进行优化。
readOnly并不是所有数据库都支持。
@Transaction(readOnly = true) //readOnly = true把当前事务属性设置为只读,默认为false

|
加了只读注解后,会有哪些影响呢?
比如做报表或者做统计:
只读事务的好处,作为ORM框架优化的暗号,保证读一致性,事务内不允许DML操作,否则报错 只读事务的场景,如统计,保证统计结果准确性。 只读事务里,也可以在只读事务里使用 select... for update 因为只读事务,所有查询都是在一个事务里,所以可以配合mysql的事务隔离级别理解一下 (比如,你的mysql隔离事务是RR的,那么在只读事务注解里,多次查询同一个表的范围数据, 结果是一致的,如果不是在同一个事务里,那么前后查询可能会读到的数据条数不一致,造成幻读),如果隔离级别是RC的,可以不用在只读事务里,因为每次查询都会读取到已提交的数据 |
如果一个类中每一个方法上都使用了@Transactional注解,那么就可以将@Transactional注解提取到类上,反过来说:@Transactional注解在类级别上标记,会影响到类中的每一个方法。同时,类级别标记的@Transactional注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上由设置了@Transactional注解。
对一个方法来说,离它最近的@Transactional注解中的事务属性设置生效。
在类级别@Transactional注解中设置只读,这样类中所有的查询方法都不需要设置@Transactional注解了,因为对查询操作来说,其他属性通常不需要设置,所以使用公共设置即可。
然后在这个基础上,对增删改方法设置@Transactional注解readOnly属性为false。
事务在执行过程中,有可能因为遇到某些问题,导致程序卡主,从而长时间占用数据库资源,大概的原因可能是因为程序运行出现了问题(Java或是MySQL)或是网络出现问题。
此时,这个很可能出问题的程序应该被执行回滚操作,撤销它已做的操作,事务回滚,把资源让出来,让其他正常程序可以执行。
总计:超时回滚,释放资源。别让一个事务占用一个资源太长的时间。

单位是秒。



默认情况:只针对运行时异常进行事务回滚,编译时异常不回滚。
//抛出编译时异常,测试是否回滚
new FileInputStream(“xxxxx”); 方法后面throws FileNotFoundException
将回滚的异常扩大到Exception的范围。




事务的隔离级别和事务的传播行为,都是指事务和事务之间的关系。 之前说的事务的属性,超时,回滚,只读都是事务考虑一个事务内部之前是事情。



我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t