MyBatis教程

1283次阅读  |  发布于5年以前

MyBatis是一个优秀的ORM(对象关系映射)框架,和Hibernate不太一样,Hibernate不用你编写一行SQL语句,而MyBatis需要手写SQL语句,优点就是能够灵活调试SQL语句。这篇文章主要对MyBatis的几个核心概念进行介绍,以及如何在项目中使用MyBatis。

MyBatis底层原理

MyBatis底层还是依赖JDBC来操作数据库,不过对JDBC的语法进行了封装,使用JDBC来操作数据库的流程一般是加载JDBC驱动->创建JDBC连接->创建Statement->执行查询->解析ResultSet->异常处理->关闭Statement和Connection,如果每次都这样做就比较繁琐。

MyBatis所有的数据库操作都是在SqlSession中操作,这样的话就可以缓存查询结果、不用重复创建JDBC连接。

MyBatis核心概念

SqlSessionFactoryBuilder

顾名思义就是采用了Builder模式,用于创建SqlSessionFactory对象。

SqlSessionFactory

SqlSessionFactory工厂方法,用于创建SqlSession。

SqlSession

SqlSession 的实例不是线程安全的,每个线程都应该有它自己的SqlSession 实例,应该随着HttpServletRequest的创建而创建,请求结束则销毁。

Mapper

承载了实际的业务逻辑,其生命周期比较短,由SqlSession创建,用于将Java对象和实际的SQL语句对应起来。

Spring环境下Mybatis初始化过程

在开发过程中通常结合Spring框架一起使用提高开发效率,由于Spring进行了控制反转,所以其中MyBatis的初始化过程和正常过程稍稍有些不同:

Spring框架会在classpath下找到MyBatis的核心配置文件,使用它来初始化一个SqlSessionFactory实例。mapper类也会作为bean注入到代码中去的,Spring会使用上一步创建的SqlSessionFactory实例来创建SqlSession的实例,然后再使用SqlSession尝试创建各个mapper对象。

于此同时,MyBatis会扫描classpath下的mapper映射XML文件(此路径可以自定义),对于每一个mapper接口,它的「类全名」会作为命名空间,来和映射文件中的mapper标签进行匹配。
对于每一个映射文件中的一个执行语句标签(如select、delete),MyBatis会把他们映射到SqlSession的方法上,创建mapper接口的一个实现类。
如果mapper接口和其映射文件一一匹配,则bean创建成功。

Mapper文件举例

下面的例子中首先创建了一个id为BaseResultMap的ResultMap,和Java模型数据的User相对应,并声明了数据库中列和模型的属性之间的对应关系,后面的Select语句可以引用这个ResultMap作为方法的返回值。然后创建了一个Base_Column_List的SQL语句,后面可以通过refid来引用这个SQL。具体的用法可以参考MyBatis文档

需要注意的是下面中的各个select、insert、update、delete的id需要和实际的com.ezlippi.mapper.UserMapper类中的方法一一对应。

<mapper namespace="com.ezlippi.mapper.UserMapper" >  

        <!--定义了Select语句返回结果和实际POJO对象的属性映射关系-->  
        <resultMap id="BaseResultMap" type="com.ezlippi.model.User" >  
            <id column="id" property="id" jdbcType="BIGINT" />  
            <result column="userName" property="userName" jdbcType="VARCHAR" />  
            <result column="passWord" property="passWord" jdbcType="VARCHAR" />  
            <result column="user_sex" property="userSex" javaType="com.ezlippi.enums.UserSexEnum"/>  
            <result column="nick_name" property="nickName" jdbcType="VARCHAR" />  
        </resultMap>  

        <!-- 定义了一条SQL语句模板,后面可以通过<include refid="Base_Column_List"/>引用这条SQL -->  
        <sql id="Base_Column_List" >  
            id, userName, passWord, user_sex, nick_name  
        </sql>  

        <select id="getAll" resultMap="BaseResultMap"  >  
           SELECT   
           <include refid="Base_Column_List" />  
           FROM users  
        </select>  

        <select id="getOne" parameterType="java.lang.Long" resultMap="BaseResultMap" >  
            SELECT   
           <include refid="Base_Column_List" />  
           FROM users  
           WHERE id = #{id}  
        </select>  

        <insert id="insert" parameterType="com.ezlippi.model.User" >  
           INSERT INTO   
                   users  
                   (userName,passWord,user_sex)   
               VALUES  
                   (#{userName}, #{passWord}, #{userSex})  
        </insert>  

        <update id="update" parameterType="com.ezlippi.model.User" >  
           UPDATE   
                   users   
           SET   
               <if test="userName != null">userName = #{userName},</if>  
               <if test="passWord != null">passWord = #{passWord},</if>  
               nick_name = #{nickName}  
           WHERE   
                   id = #{id}  
        </update>  

        <delete id="delete" parameterType="java.lang.Long" >  
           DELETE FROM  
                    users   
           WHERE   
                    id =#{id}  
        </delete>  
    </mapper>  

#{}和${}的区别

在拼接SQL语句中可以用"#{}"和"${}"来引用方法中的参数,两者的区别如下:

"#{}"在底层实现上使用?做占位符来生成PreparedStatement,然后将参数传入,大多数情况都应使用这个,它更快、更安全。

"${}"将传入的数据直接显示生成在sql中。如:order by ${user_id},如果传入的值是111,那么解析

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:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">    

        <!-- 加载配置文件 -->  
        <context:property-placeholder location="classpath:jdbc.properties" />  

        <!-- 配置dbcp数据源 引用jdbc.peoperties属性文件中的属性-->    
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">    
            <property name="driverClass" value="${jdbc.driver}" />    
            <property name="jdbcUrl" value="${jdbc.url}" />    
            <property name="user" value="${jdbc.username}" />    
            <property name="password" value="${jdbc.password}" />    
            <property name="initialPoolSize" value="${connection_pools.initial_pool_size}" />  
            <property name="minPoolSize" value="${connection_pools.min_pool_size}" />  
            <property name="maxPoolSize" value="${connection_pools.max_pool_size}" />  
            <property name="maxIdleTime" value="${connection_pools.max_idle_time}" />  
            <property name="acquireIncrement" value="${connection_pools.acquire_increment}" />  
            <property name="checkoutTimeout" value="${connection_pools.checkout_timeout}" />  
        </bean>    

        <!-- 配置mybatisSqlSessionFactoryBean -->    
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">    
            <property name="dataSource" ref="dataSource" />    
            <property name="configLocation" value="classpath:mybatis/mybatis.xml"/>    
            <property name="mapperLocations" value="classpath*:com/ezlippi/**/*Mapper.xml"/>  
        </bean>    

        <!-- 配置SqlSessionTemplate -->    
        <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">    
            <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />    
        </bean>    

        <!-- mapper批量扫描,从mapper包中扫描出mapper接口,自动创建代理对象并且在spring容器中注册   
        遵循规范:将mapper.java和mapper.xml映射文件名称保持一致,且在一个目录 中  
        自动扫描出来的mapper的bean的id为mapper类名(首字母小写)  
        -->  
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
            <property name="basePackage" value="com.ezlippi.**.dao"/>  
            <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>  
        </bean>  

        <!-- 事务配置 -->    
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
            <property name="dataSource" ref="dataSource" />    
        </bean>    

        <!-- 使用annotation注解方式配置事务 -->    
        <tx:annotation-driven transaction-manager="transactionManager"/>    


        <context:component-scan base-package="com.ezlippi" />  
        <context:annotation-config />  

    </beans>  

其中如下代码为spring自扫描所有dao包并把其下的所有mybatis接口文件装配入容器。

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
            <property name="basePackage" value="com.ezlippi.**.dao"/>  
            <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>  
    </bean>  

mybatis配置文件mybatis.xml如下,主要配置一些alias:

<configuration>  
        <typeAliases>  
            <typeAlias alias="Integer" type="java.lang.Integer" />  
            <typeAlias alias="Long" type="java.lang.Long" />  
            <typeAlias alias="HashMap" type="java.util.HashMap" />  
            <typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap" />  
            <typeAlias alias="ArrayList" type="java.util.ArrayList" />  
            <typeAlias alias="LinkedList" type="java.util.LinkedList" />  
        </typeAliases>  
    </configuration>  

mybatis接口mapper需要加@Repository注解,方可直接在Service层直接自动装配注入。 mybatis接口mapper举例如下:

@Repository  
    public interface UserMapper {  

    List<User> getAll();  

    User getOne(Long id);  

    void insert(User user);  

    void update(User user);  

    void delete(Long id);  

    }  

Service层注入mybatis接口mapper的时候需要在构造方法中注入,这样注入mapper实例才不会空。

@Service(value = "userService")  
    public class UserServiceImpl {  

        private UserMapper mapper;  

        @Autowired  
        public UserServiceImpl(UserMapper userMapper) {  
            this.mapper = userMapper;  
        }  

        public List<User> selectAll() {  
            return userMapper.selectAll();  
        }   
    }  

看到这里,可能会有一个疑问,上面我只声明了UserMapper接口,没有定义实现类怎么调用具体的方法呢,别担心,mybatis通过JDK的动态代理方式,在启动加载配置文件时,根据配置mapper的xml去生成Dao的实现。
session.getMapper()使用了代理,当调用一次此方法,都会产生一个代理class的instance。

最后就是UserControl类了,调用UserService来完成具体的业务逻辑,

@RestController  
    public class UserControl {  

        @Autowired  
        private UserService userService;  

        @RequestMapping("/users/all")  
        public List<User> getAllUsers() {  
            return userService.selectAll();  
        }  
    }  

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8