spring详解-下篇(IDEA版)

如果你没有梦想,那么你只能为别人的梦想打工。你好,我是梦阳辰!期待与你相遇!

01.使用注解开发

在spring4之后,要使用注解开发,必须保证aop包导入。

使用注解需要导入context约束,增加注解的支持!

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--指定要扫描的包,这个报下的注解就会生效-->
    <context:component-scan base-package="com.mengyangchen.pojo"/>
    <!--开启注解的支持-->
    <context:annotation-config/>



</beans>

@Component:
组件,放在类上,说明这个类被spring管理了,就是bean.

@Value

package com.mengyangchen.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/*等价于<bean id="user" class="com.mengyangchen.pojo.User/>*/
@Component
public class User {
    /*相当于<property name="name" value="梦阳辰"/>*/
    @Value("梦阳辰")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

注解的衍生:
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层。

dao:【@Repository】
service:【@Service】
controller:【@Controller】

这四个注解功能是相同的,都是代表将某个类注册到Spring中,装配Bean

自动装配:
在这里插入图片描述
作用域
@Scope(“singleton”)//单例

小结:
xml更加万能,适用于任何场合。维护简单方便。
注解:不是自己类使用不了,维护相对复杂。

可以采用:
xml管理bean,注解负责完成属性的注入。

使用java的方式配置spring

完全不适用spring的xml配置,全部交给java来做。
JavaConfig是spring的一个子项目,在spring4之后,他成为了一个核心功能。

1.不需要xml配置

/*这个也会被spring容器托管,注册到容器中,因为本来就是一个Component
@Configuration表示这是一个配置类,跟beans.xml相同*/
@Configuration
@ComponentScan("com.mengyangchen.pojo")//扫描包
@Import(mengConfig2.class)//引入另一个配置类
public class mengConfig {

    /*注册一个bean,就相当于xml的一个bean标签
    * 方法名相当于bean标签中的id属性
    * 这个方法的返回值,就相当于bean标签的class属性*/
    @Bean
    public User getUser(){
        return new User();
    }
}

实体类:

/*等价于<bean id="user" class="com.mengyangchen.pojo.User/>*/
@Component
public class User {
    /*相当于<property name="name" value="梦阳辰"/>*/
    @Value("梦阳辰")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

测试方法:

@Test
    public void test(){
        //如果完成采用了配置类,就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
        ApplicationContext context = new AnnotationConfigApplicationContext(mengConfig.class);
        User user = context.getBean("getUser", User.class);
        System.out.println(user.getName());

    }

02.代理模式

为什么要学习?
因为这是spring的AOP的底层。
代理:相当于中介。
例如租房:
在这里插入图片描述

静态代理

角色分析:
抽象角色(代理的东西):一般会使用接口或者抽象类来解决。

真实角色:被代理的角色。

代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。

客户:访问代理对象的人。

代码步骤
1.接口(出租)

2.真实角色(房东出租)

3.代理角色(帮房东出租)

4.客户(租房)

代理模式的好处:
可以使真实角色的操作更加简单,不用去关注一些公共的业务(例如出租房子的琐事)

公共的业务(出租)也就交给代理角色!实现了业务的分工!

公共业务发生扩展的时候,方便集中管理!
缺点:
一个真实角色就会产生一个代理角色;代码量会翻倍开发效率会变低

不同的房东要将出租业务交给代理商,在程序方面我们就需要创建不同代理类,实现这个出租业务,而动态代理可以动态的创建代理类,实现完成该业务

动态代理

动态代理用反射完成,解决了静态代理的缺点。
动态代理和静态代理的角色一样。

动态代理的代理类是动态生成的,不是我们直接写的。

动态代理分为两大类:
1.基于接口的动态代理

jdk动态代理

2.基于类的动态代理。

cglib

3.java字节码实现

javassist

需要了解的两个类:

Proxy:代理
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

为某个接口创建代理Foo

  Foo f = (Foo) Proxy.newProxyInstance
  (Foo.class.getClassLoader(),new Class<?>[] { Foo.class }, handler); 
//类加载器是负责加载类的对象。 ClassLoader类是一个抽象类。 给定一个类的binary name ,类加载器应该尝试定位或生成构成类的定义的数据

//new Class<?>[] { Foo.class }代理的接口(方法)

InvocationHandler(接口):调用处理程序。
InvocationHandler是由代理实例调用处理程序实现的接口 。
方法:

Object invoke(Object proxy, 方法 method, Object[] args) 
处理代理实例上的方法调用并返回结果。  

一个动态代理代理的是一个接口,一般就是对应的一类业务。
例如:
接口:

package com.mengyangchen.demo;

public interface UserService {
    void rentHouse();
    void saleHouse();
}

房东1:

package com.mengyangchen.demo;

public class UserServiceImpl implements UserService{
    @Override
    public void rentHouse() {
        System.out.println("租房成功!");
    }

    @Override
    public void saleHouse() {
        System.out.println("购房成功!");
    }
}

房东2:

package com.mengyangchen.demo;

public class UserServiceImpl2 implements UserService {
    @Override
    public void rentHouse() {
        System.out.println("租房成功2!");
    }

    @Override
    public void saleHouse() {
        System.out.println("购房成功2!");
    }
}

自动生成代理对象的类:

package com.mengyangchen.demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    /**
     *处理代理示例,并返回结果,动态代理使用反射实现
     * @param proxy 代理对象
     * @param method 代理对象调用的方法被封装成对象
     * @param args 代理对象调用方法时,传递的实际参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //这里面可以进行操作,比如判断调用的方法名,即根据不同的方法,代理商实现一些额外操作(用if判断)
        if(method.getName().equals("rentHouse")){
            log(method.getName());
            //这里面可以进行相关操作,获取参数,处理等
            //double money = (double)args[0];如获取第一个参数
            Object result = method.invoke(target,args);
            return result;
        }else{
            log(method.getName());
            Object result = method.invoke(target,args);
            return result;
        }

    }

    public void log(String msg){
        System.out.println("执行了"+msg+"操作!");
    }
}

客户类:

package com.mengyangchen.demo;

public class Client {
    public static void main(String[] args) {
        //真实角色
        UserService userService = new UserServiceImpl2();
        //代理角色,不存在(通过平台找代理商)
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        
        pih.setTarget(userService);//设置代理对象(需要买的房子)
        //动态生成代理类(找到代理商)
        UserService proxy = (UserService)pih.getProxy();
        //调用方法(代理商卖房)
        proxy.saleHouse();
    }
}

03.AOP

什么是AOP?
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
在这里插入图片描述
Aop在Spring中的作用
提供声明式事务;允许用户自定义切面

横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…

切面(ASPECT)∶横切关注点被模块化的特殊对象。即,它是一个类。

通知(Advice)︰切面必须要完成的工作。即,它是类中的一个方法。

目标(Target)︰被通知对象。

代理(Proxy)∶向目标对象应用通知之后创建的对象。

切入点(PointCut):切面通知执行的“地点"的定义。

连接点(JointPoint) :与切入点匹配的执行点。
在这里插入图片描述
导入包:

<dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
</dependency>

使用Sprin的API接口增加日志业务

接口:

public interface UserService {
    void insert();
    void delete();
    void update();
    void select();
}

接口实现类:

package com.mengyangchen.service;

public class UserServiceImpl implements UserService{


    @Override
    public void insert() {
        System.out.println("增加了一个用户!");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户!");
    }

    @Override
    public void update() {
        System.out.println("更新了一个用户!");
    }

    @Override
    public void select() {
        System.out.println("查询了一个用户!");
    }
}

日志类:

public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"返回结果为:"+returnValue);
    }
}
public class BeforeLog implements MethodBeforeAdvice {
    /**
     *
     * @param method 要执行的目标对象的方法
     * @param args 参数
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了!");
    }
}

配置文件:

<?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:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="userService" class="com.mengyangchen.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.mengyangchen.log.BeforeLog"/>
    <bean id="afterlog" class="com.mengyangchen.log.AfterLog"/>

    <!--配置aop,需要导入aop的约束-->
    <aop:config>
        <!--切入点 expression表达式,excution(要执行的位置)-->
        <aop:pointcut id="pointcut" expression="execution(* com.mengyangchen.service.UserServiceImpl.*(..))"/>
        <!--执行环绕增加日志-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

测试:

@Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //代理的是接口
        UserService userService = (UserService)context.getBean("userService");
        userService.insert();
    }

在这里插入图片描述
使用自定义类实现日志:

public class LogDiy {
    public void before(){
        System.out.println("===前===");
    }
    public void after(){
        System.out.println("===后===");
    }
}

 <!--方法二:自定义类-->
    <bean id="diyLog" class="com.mengyangchen.diy.LogDiy"/>
    <aop:config>
        <!--自定义切面 ,ref要引用的类-->
        <aop:aspect ref="diyLog">
            <aop:pointcut id="point" expression="execution(* com.mengyangchen.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

在这里插入图片描述
使用注解实现
在这里插入图片描述
在这里插入图片描述
导包:

04.整合Mybatis

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>


    </dependencies>

Mybatis-spring
http://mybatis.org/spring/zh/index.html
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。

1.编写数据源配置

2.sqlSessionFactory

3.sqlSessionTemglate

4.需要给接口加实现类

5.将自己写的实现类,注入到Spring中

6.测试使用即可!

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

要注意的是 SqlSessionFactoryBean 实现了 Spring 的 FactoryBean 接口, 这意味着由 Spring 最终创建的 bean 并不是 SqlSessionFactoryBean 本身,而是工厂类(SqlSessionFactoryBean)的 getObject() 方法的返回结果。这种情况下,Spring 将会在应用启动时为你创建 SqlSessionFactory,并使用 sqlSessionFactory 这个名字存储起来。

通常,在 MyBatis-Spring 中,你不需要直接使用 SqlSessionFactoryBean 或对应的 SqlSessionFactory。 相反,session 的工厂 bean 将会被注入到 MapperFactoryBean 或其它继承于 SqlSessionDaoSupport 的 DAO(Data Access Object,数据访问对象)中。

SqlSessionFactory 有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。

一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 <settings><typeAliases> 元素。

需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(<environments>),数据源(<DataSource>)和 MyBatis 的事务管理器(<transactionManager>)都会被忽略。 SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。

如果 MyBatis 在映射器类对应的路径下找不到与之相对应的映射器 XML 文件,那么也需要配置文件。这时有两种解决办法:第一种是手动在 MyBatis 的 XML 配置文件中的 部分中指定 XML 文件的类路径;第二种是设置工厂 bean 的 mapperLocations 属性。mapperLocations 属性接受多个资源位置。这个属性可以用来指定 MyBatis 的映射器 XML 配置文件的位置。属性的值是一个 Ant 风格的字符串,可以指定加载一个目录中的所有文件,或者从一个目录开始递归搜索所有目录。比如:

bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" />
</bean>

这会从类路径下加载所有在 sample.config.mappers 包和它的子包中的 MyBatis 映射器 XML 配置文件。

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。 SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。

当调用 SQL 方法时(包括由 getMapper() 方法返回的映射器中的方法),SqlSessionTemplate 将会保证使用的 SqlSession 与当前 Spring 的事务相关。 此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的 DataAccessExceptions。

由于模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。

可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

练习:
实体类:

package com.mengyangchen.pojo;

public class User {
    private Integer id;
    private String name;
    private String password;

    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

接口:

public interface UserMapper {
    List<User> selectUser();
}

接口实现类:

public class UserMapperImpl implements UserMapper{
    /*在mybatis中所有的操作都是使用sqlSession来执行,现在都是用SqlSessionTemplate*/

    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

接口的sql配置文件:

<?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.mengyangchen.mapper.UserMapper">
    <!--结果集映射-->
    <!--column数据库中的字段,property实体类中的属性-->
    <resultMap id="userMap" type="user">
        <result column="pwd" property="password"/>
    </resultMap>

    <select id="selectUser" resultMap="userMap">
    select * from mybatis.user
  </select>

</mapper>

spring的连接数据库文件(spring-dao.xml):

<?xml version="1.0" encoding="UTF-8"?>
<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
    我们这里使用Spring提供的JDBC:org.springframework.jdbc.datasource-->

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--绑定Mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--配置mapper-->
        <property name="mapperLocations" value="classpath:com/mengyangchen/mapper/*.xml"/>
    </bean>

    <!--SqlSessionTemplate:就是我们使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--使用构造器注入,因为没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

</beans>

spring的总配置文件(applicationContext.xml):

<?xml version="1.0" encoding="UTF-8"?>
<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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="spring-dao.xml"/>

    <bean id="userMapper" class="com.mengyangchen.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>


</beans>

mybatis的配置文件:

<?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="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--别名-->
    <typeAliases>
        <package name="com.mengyangchen.pojo"/>
    </typeAliases>


</configuration>

测试:

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserMapper userMapper  = context.getBean("userMapper",UserMapper.class);
        for(User user:userMapper.selectUser()){
            System.out.println(user);
        }
    }

结果:
在这里插入图片描述
SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法,就像下面这样:

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
  public User getUser(String userId) {
    return getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

在这个类里面,通常更倾向于使用 MapperFactoryBean,因为它不需要额外的代码。但是,如果你需要在 DAO 中做其它非 MyBatis 的工作或需要一个非抽象的实现类,那么这个类就很有用了。

SqlSessionDaoSupport 需要通过属性设置一个 sqlSessionFactory 或 SqlSessionTemplate。如果两个属性都被设置了,那么 SqlSessionFactory 将被忽略。

假设类 UserMapperImpl 是 SqlSessionDaoSupport 的子类,可以编写如下的 Spring 配置来执行设置:

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

例如:

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
    @Override
    public List<User> selectUser() {
        /*SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();*/
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

spring的配置文件。

    <bean id="userMapper2" class="com.mengyangchen.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

05.事务(Transaction)

事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

将一组业务当成一个业务来完成(要么成功,要么失败)。

切薄完整性和一致性。

事务的的ACID原则:
原子性
业务不可分割(一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。)

一致性
从一个一致性状态到另一个一致性状态
要么成功,要么失败

隔离性
一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
多个业务可能操作同一个资源,(不被其他事务干扰)防止数据损坏。

持久性
持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
事务一旦提交,无论系统发生什么问题,结果都不会被影响,被持久化写到存储器中。

MyBatis-Spring事务管理:
一个使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。

一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。

事务配置好了以后,MyBatis-Spring 将会透明地管理事务。这样在你的 DAO 类中就不需要额外的代码了。

要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="dataSource" />
</bean>
@Configuration
public class DataSourceConfig {
  @Bean
  public DataSourceTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource());
  }
}

交由容器管理事务(AOP的方式)
如果你正使用一个 JEE 容器而且想让 Spring 参与到容器管理事务(Container managed transactions,CMT)的过程中,那么 Spring 应该被设置为使用 JtaTransactionManager 或由容器指定的一个子类作为事务管理器。最简单的方式是使用 Spring 的事务命名空间或使用 JtaTransactionManagerFactoryBean:

如果我们在spring中去配置声明式事务,我们就需要在代码中手动配置事务。

练习:
spring配置

  <bean id="userMapper3" class="com.mengyangchen.mapper.UserMapperImpl3">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <!--配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
    </bean>

    <!--结合AOP实现事务的注入-->
    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--给哪些方法配置事务-->
        <!--配置事务的传播特性 propagation-->
        <tx:attributes>
            <tx:method name="insert" propagation="REQUIRED"/>
            <tx:method name="delete"/>
            <tx:method name="update"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*"/><!--所有方法开启事务-->
        </tx:attributes>
    </tx:advice>

    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.mengyangchen.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

实现类:

package com.mengyangchen.mapper;

import com.mengyangchen.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl3  extends SqlSessionDaoSupport implements  UserMapper {
    @Override
    public List<User> selectUser() {
        User user = new User(5,"小梦","123456");
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
        mapper.insertUser(user);
        mapper.deleteUser(5);
        return mapper.selectUser();
    }

    @Override
    public int insertUser(User user) {
        return getSqlSession().getMapper(UserMapper.class).insertUser(user);
    }

    @Override
    public int deleteUser(int id) {
        return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
    }
}

测试:

 @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserMapper userMapper  = context.getBean("userMapper3",UserMapper.class);
        for(User user:userMapper.selectUser()){
            System.out.println(user);
        }
    }

编程式事务管理
MyBatis 的 SqlSession 提供几个方法来在代码中处理事务。但是当使用 MyBatis-Spring 时,你的 bean 将会注入由 Spring 管理的 SqlSession 或映射器。也就是说,Spring 总是为你处理了事务。

你不能在 Spring 管理的 SqlSession 上调用 SqlSession.commit(),SqlSession.rollback() 或 SqlSession.close() 方法。如果这样做了,就会抛出 UnsupportedOperationException 异常。在使用注入的映射器时,这些方法也不会暴露出来。

无论 JDBC 连接是否设置为自动提交,调用 SqlSession 数据方法或在 Spring 事务之外调用任何在映射器中方法,事务都将会自动被提交。
下面的代码展示了如何使用 PlatformTransactionManager 手工管理事务。

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionStatus txStatus =
        transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
      userMapper.insertUser(user);
    } catch (Exception e) {
      transactionManager.rollback(txStatus);
      throw e;
    }
    transactionManager.commit(txStatus);
  }
}

在使用 TransactionTemplate 的时候,可以省略对 commit 和 rollback 方法的调用

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    transactionTemplate.execute(txStatus -> {
      userMapper.insertUser(user);
      return null;
    });
  }
}

keep trying no matter how hard it seems. it will get easier.

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值