本文编写于 123 天前,最后修改于 123 天前,其中某些信息可能已经过时。

回忆Mybatis

@Data
public class User {
    private Integer id;
    private String name;
    private String pwd;
}
@Data
public class User {
    private Integer id;
    private String name;
    private String pwd;
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springagain01.mapper.UserMapper">
    <select id="select" resultType="user">
        select * from mybatis.user;
    </select>
</mapper>
<?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>
    <typeAliases>
        <package name="com.springagain01.pojo"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456789z"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper class="com.springagain01.mapper.UserMapper"/>
    </mappers>
</configuration>

整合Mybatis

我来想想是直接上代码,还是梳理一下思路。毕竟mybatis也没个笔记都快忘了。

先来聊聊原生的mybatis。

  1. 它首先需要一个xml文件来配置你的数据源以及决定事务作用域和控制方式的事务管理器(TransactionManager)(在spring-mybatis中将会被Spring数据源所替换)。
  2. 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

    String resource = "org/mybatis/example/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  3. 这时候我们需要建立一个接口来表示我们想要实现的功能。

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

然后我们再建立一个xxxMapper.xml的文件来实现它

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springagain01.mapper.UserMapper">
    <select id="select" resultType="user">
        select * from mybatis.user;
    </select>
</mapper>
  1. 最后我们就可以使用它了。

    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.select();

## 在Spring中整合mybatis

相关jar包,mybatis-spring,spring-jdbc,mysql-connector-java

上述的建立接口,建立mapper.xml不用变化,

我们所做的只有:

  1. 更换数据源

    <!--        使用Spring的数据源替换mybatis的配置,别忘了导入Spring-jdbc的包-->
       <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
     <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF8"/>
     <property name="username" value="root"/>
     <property name="password" value="123456789z"/>
       </bean>
  2. 干掉代码中的SqlSessionFactory(重点是注入数据源)

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
         <property name="dataSource" ref="datasource" />
    <!--            可加可不加-->
    <!--            绑定mybatis配置文件,别名管理和设置一般放在mybatis配置文件中-->
         <property name="configLocation" value="classpath:mybatis-config.xml"/>
         <property name="mapperLocations" value="classpath:com/springagain01/mapper/*.xml"/>
     </bean>
  3. 书接上文,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。可见sqlsession的重要性,在这里我们使用SqlSessionTemplate。

    <!--    SqlSessionTemplate:就是sqlsession,由于没有set方法请使用构造函数注入-->
     <bean id="sqlsession" class="org.mybatis.spring.SqlSessionTemplate">
         <constructor-arg index="0" ref="sqlSessionFactory"/>
     </bean>
  4. 将实现类装配到Spring中

    <bean id="userMapper" class="com.springagain01.mapper.UserMapperImpl">
       <property name="sqlSession" ref="sqlsession"/>
    </bean>
  5. 由于Spring的ioc思想我们需要建立一个接口实现类将实现交给Spring来管理(使用set注入)

  6. class UserMapperImpl implements UserMapper {

    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {

    this.sqlSession = sqlSession;

    }

    @Override
    public List<User> select() {

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    return mapper.select();

    }
    }

下面写个完整版

@Data
public class User {
    private Integer id;
    private String name;
    private String pwd;
}
public interface UserMapper {
    List<User> select();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springagain01.mapper.UserMapper">
    <select id="select" resultType="user">
        select * from mybatis.user;
    </select>
</mapper>
public class UserMapperImpl implements UserMapper {

    private SqlSessionTemplate sqlSession;

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

    @Override
    public List<User> select() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.select();
    }
}
public class Test {
    @org.junit.Test
    public void test() throws IOException {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

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

下面是配置文件

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

    <!--        使用Spring的数据源替换mybatis的配置,别忘了导入Spring-jdbc的包-->
        <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456789z"/>
        </bean>
<!--    这里就可以直接把代码中的SqlSessionFactory干掉了-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="datasource" />
<!--            可加可不加-->
<!--            绑定mybatis配置文件,别名管理和设置一般放在mybatis配置文件中-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:com/springagain01/mapper/*.xml"/>
        </bean>
<!--    SqlSessionTemplate:就是sqlsession,由于没有set方法请使用构造函数注入-->
        <bean id="sqlsession" class="org.mybatis.spring.SqlSessionTemplate">
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </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>

    <typeAliases>
        <package name="com.springagain01.pojo"/>
    </typeAliases>

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

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

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

由于mybatis基础的原因这部分可能写的不是很好。

还有一个重要的问题,有时候mapper文件夹下的xml文件不会被打包到服务器,所以我们这时候就要解决maven静态资源加载问题

<build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

在Spring-Mybatis官网给我们提供了简化版,也就是简化了注入过程

我们来继承SqlSessionDaoSupport,确实没有了注入过程

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> select() {
         return getSqlSession().getMapper(UserMapper.class).select();
    }
}

我们新增了一个类把它配置到Spring中

<bean id="userMapper2" class="com.springagain01.mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
public class Test {
    @org.junit.Test
    public void test() throws IOException {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

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

事务的ACID原则别忘

我们当然不能只有查询方法,接下来来说一下为何要开启事务。

我们来添加一个增加和删除功能

public interface UserMapper {
    List<User> select();
    int addUser(User user);
    int delete(int id);
}

同时要在xml中添加,这里故意将delete写为deletes

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springagain01.mapper.UserMapper">
    <select id="select" resultType="user">
        select * from mybatis.user;
    </select>
    <insert id="addUser" parameterType="user">
        insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
    </insert>
    <delete id="delete" parameterType="int">
        deletes from mybatis.user where id = #{id}
    </delete>
</mapper>
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {

    @Override
    public List<User> select() {
        User user = new User(6,"wo","fa");
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);

        mapper.addUser(user);
        mapper.delete(6);
        return getSqlSession().getMapper(UserMapper.class).select();

    }

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

    @Override
    public int delete(int id) {
        return getSqlSession().getMapper(UserMapper.class).delete(id);
    }
}
public class Test {
    @org.junit.Test
    public void test() throws IOException {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

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

经过测试数据确实插入进去了,但是并没有删除掉,这样显然并不是我们想要的结果。

我们要根据事务的A:原子性

一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作。

来改写这段代码。

在Spring中事务管理有

  1. 声明式事务:AOP
  2. 编程式事务:需要在代码中,进行事务管理,这并不是我们想要的,我们想要的是不改动原有代码的情况下尽可能地拓展。
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--        使用Spring的数据源替换mybatis的配置,别忘了导入Spring-jdbc的包-->
        <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456789z"/>
        </bean>
<!--    这里就可以直接把代码中的SqlSessionFactory干掉了-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="datasource" />
<!--            可加可不加-->
<!--            绑定mybatis配置文件,别名管理和设置一般放在mybatis配置文件中-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:com/springagain01/mapper/*.xml"/>
        </bean>
<!--    SqlSessionTemplate:就是sqlsession,由于没有set方法请使用构造函数注入-->
        <bean id="sqlsession" class="org.mybatis.spring.SqlSessionTemplate">
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>


        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <constructor-arg ref="datasource" />
        </bean>

        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
        <aop:config>
            <aop:pointcut id="txPointcut" expression="execution(* com.springagain01.mapper.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
        </aop:config>
</beans>

配置事务开启后,再改动delete,也不会出现错误的插入数据。

一荣俱荣,一损俱损。