Java持久化框架-MyBatis应用

  MyBatis是支持定制化SQL、存储过程以及高级映射的优秀持久层框架。MyBatis几乎避免了所有的JDBC代码和手动设置参数,MyBatis可以对配置使用简单的XML或注解,将接口和Java的POJOs(普通Java对象)映射成 数据库中的记录。

一、添加依赖

  如果使用Maven来构建项目,则需要引入如下两个依赖:

<!-- 引入mabatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.3.1</version>
</dependency>

<!-- 引入JDBC -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.41</version>
</dependency>

二、properties属性文件(config.properties)

  JDBC连接需要的driver、url以及用户信息等配置可以在Java属性文件中配置,config.properties文件配置如下:

driver=com.mysql.jdbc.Driver
## format -> jdbc:mysql://<host>:<port>/<database_name>?property1=value1&property2=value2
url=jdbc:mysql://localhost:3306/test
username=root
password=123

  在MyBatis的XML配置文件中可以通过properties标签引入,引入后属性文件定义的值可以通过${key}方式在XML文件中引用。

<configuration>
...
    <properties resource="config.properties">
        <property name="username" value="root" />
        <property name="password" value="456" />
    </properties>
...
</configuration>    

  注意,properties标签除了通过resource引入外部properties文件的属性外,也可通过property标签定义属性。对于同名key,外部资源文件优先级高于property标签。

三、XML配置文件 (config.xml)

  本文使用的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>

    <properties resource="config.properties">
        <property name="username" value="root" />
        <property name="password" value="456" />
    </properties>

    <typeAliases>
        <typeAlias alias="User" type="com.xiaofan.test.User" />
    </typeAliases>

    <environments default="development"> <!-- 默认环境ID,如:default="developent" -->
        <environment id="development"> <!-- 每个environment元素定义的环境ID, 如:id="developent" -->
            <transactionManager type="JDBC"/> <!-- 事务管理器的配置,如:type="JDBC" -->
            <dataSource type="POOLED"> <!-- 数据源配置,如:type="POOLED" -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="user_mapper.xml"/>
    </mappers>
</configuration>

  首先,MyBatis的配置文件顶层结构如下:

- configuration     - properties     - settings     - typeAliases     - typeHandlers     - objectFactory     - plugins     - environments         - environment             - transactionManager             - dataSource     - databaseIdProvider     - mapper
  • properties

      见上节

  • settings

      settings标签是MyBatis中重要的参数调整设置标签,可通过设置其属性改变MyBatis的运行时行为。本文均采用MyBatis默认设置,详细参数设置见文档

  • typeAliases

      类别名是为Java类设置一个短的名字,它存在的意义仅在用于减少类完全限定名的冗余。如下:

<configuration>
...
    <typeAliases>
        <typeAlias alias="User" type="com.xiaofan.test.User" />
    </typeAliases>
...
</configuration>

  在mapper.xml文件中可将com.xiaofan.test.User全路径简写。如下:

<mapper namespace="com.xiaofan.test.UserDAO">
...
    <!-- resultType="com.xiaofan.test.User" -->
    <select id="findByName" resultType="User">
        select * from user where name=#{name}
    </select>
...
</mapper>
  • typeHandlers

      无论是MyBatis在预处理中设置一个参数,还是从结果集中取一个值,都会用类型处理器将获取的值以合适的方式转换成Java类型,部分常用的默认处理器如下:

处理器JDBC类型Java类型
BooleanTypeHandlerBOOLEANjava.lang.Boolean, boolean
IntegerTypeHandlerNUMERIC, INTEGERjava.lang.Integer, int
LongTypeHandlerNUMERIC, LONG INTEGERjava.lang.Long, long
FloatTypeHandlerNUMERIC, DOUBLEjava.lang.Double, double
StringTypeHandlerCHAR, VARCHARjava.lang.String
DateTypeHandlerTIMESTAMPjava.util.Date

  除了以上默认类型处理器件外,也可以通过实现org.apache.ibatis.type.TypeHandler或继承org.apache.ibatis.type.BaseTypeHandler来重写或创建自己的类型处理器。配置文件如下

<configuration>
...
    <typeHandlers>
        <typeHandler handler="your handler path" />
    </typeHandlers>
...
</configuration>
  • objectFactory

      MyBatis每次创建结构对象的新实例时,都会使用一个对象工厂(objectFactory)实例来完成,默认的对象工厂仅仅是实例化目标类,如果参数映射不存在,则通过默认构造方法完成实例化,如果存在参数映射,则通过参数构造方法完成实例化。也可以通过创建自己的对象工厂完成实例化。

  • plugins

    MyBatis允许在已映射语句执行过程中的某一点进行拦截,默认情况下,MyBatis允许使用插件来拦截的方法包括:Executor、ParameterHandler、ResultSetHandler、StatementHandler。

  • environments

      MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射用于多种数据库,每个环境需要创建一个独立的SqlSessionFactory实例,每个数据库对应一个。一份MyBatis环境配置示例如下:

<configuration>
...
    <environments default="development"> <!-- 默认环境ID,如:default="developent" -->
        <environment id="development"> <!-- 每个environment元素定义的环境ID, 如:id="developent" -->
            <transactionManager type="JDBC"/> <!-- 事务管理器的配置,如:type="JDBC" -->
            <dataSource type="POOLED"> <!-- 数据源配置,如:type="POOLED" -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
...
<configuration>

  MyBatis中有两种类型的事务管理器(type=”[JDBC|MANAGED]”)。
  JDBC:使用JDBC的提交和回滚设置,它依赖于数据源得到的连接来管理事务作用域;
  MANAGED:它不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。如果使用spring+MyBatis,则没必要配置事务管理器,因为Spring会使用自带的管理器覆盖前面的设置。

  MyBatis有三种内建的数据源类型(type=”[UNPOOLED|POOLED|JNDI]”)。
  UNPOOLED:顾名思义,该数据源类型将不使用连接池,每次被请求时打开和关闭连接。缺点时慢,它对没有性能要求的简单应用程序中是个很好的选择。它的必要属性:
    driver:JDBC驱动的Java类完全限定名
    url:数据库JDBC 的URL地址
    username:登录数据库的用户名
    password:登录数据库的密码
    defaultTransactionIsolationLevel:默认的连接事务隔离级别

  POOLED:该类型利用数据库连接池,避免了创建新数据库连接时需要的初始化和认证时间。这是兵法Web应用快速响应请求的流行处理方式。
     poolMaximumActiveConnections:任意时间可存在的连接数,默认:10
     poolMaximumIdleConnections:任意时间可存在的空闲连接数
     poolMaximumCheckoutTime:被强制返回前,池中连接被checkout时间,默认:20秒
     poolPingQuery:发送侦测查询到数据库。默认:NO PING QUERY SET
     poolPingEnabled:是否启用侦测查询。默认:false
     poolPingConnectionsNotUsedFor:poolPingQuery的使用频度。默认:0
  JNDI:该数类型的实现是为了能在如EJB或应用服务器这类容器中使用,容器可集中或在外部配置数据源,然后放置在一个JNDI上下文中。

  • databaseIdProvider

      MyBatis可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的databaseId属性。

  • mappers

      mappers标签是让MyBatis查找映射文件,从而能够获取到SQL映射语句。其中映射文件的路径可以是资源文件路径的相对路径,也可以是绝对路径。使用范例如下

<configuration>
...
    <mappers>
        <mapper resource="user_mapper.xml"/> <!-- resource相对路径-->
    </mappers>
...
</configuration>

注意,properties、setting、typeAliases等configuration子标签必须按以上顺序设置,顺序设置错误会导致如下错误:

org.xml.sax.SAXParseException; ... 元素类型为 "configuration" 的内容必须匹配 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"

四、XML映射文件 (user_mapper.xml)

  MyBatis的SQL映射功能强大,它能极大的简化SQL构建。SQL映射文件的顶级元素如下:

cache : 给定命名空间的缓存配置
cache-ref : 其他命名空间缓存配置的引用
resultMap : 描述如何从数据库结果集中加载对象
sql : 可以被其他语句引用的可重用语句块
insert : 插入语句 update : 更新语句 delete : 删除语句 select : 查询语句

  本文demo使用的一个简单的MyBatis XML映射文件如下:

<?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.xiaofan.test.UserDAO">

    <resultMap id="UserResultMap" type="User">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
    </resultMap>

    <sql id="tmpSql" >
        select * from ${realdb}
    </sql>

    <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into user(name,age) values(#{name},#{age})
    </insert>

    <insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
        insert into user(name, age) values
        <foreach item="item" collection="list" separator=",">
            (#{item.name}, #{item.age})
        </foreach>
    </insert>

    <update id="update" parameterType="User">
        update user set name=#{name},age=#{age} where name=#{name}
    </update>

    <delete id="delete" parameterType="String">
        delete from user where name=#{name}
    </delete>

    <select id="findByName" resultMap="UserResultMap">
        <include refid="tmpSql">
            <property name="realdb" value="user" />
        </include>
        where name=#{name}
    </select>

    <select id="selectAll" resultType="User">
        select * from user
    </select>

</mapper>

insert/update/delete/select

  以上配置文件中的select、insert、update、delete元素没有使用太多属性,其实,MyBatis为CRUD元素提供了丰富的属性可选。部分常用属性配置如下

属性描述默认值
id在命名空间中唯一的标识符,可用该标识来引用这条语句
parameterType[可选] 当前语句参数类的完全限定名或别名,MyBatis可通过TypeHandler推断unset
resultType返回的期望类型类的完全限定名或别名。注意:如果是集合,则resultType是集合包含的类型,如List中的E
resultMap引用外部定义的resultMap,如果是集合情况如resultMap。resultType和resultMap不可同时使用
timeout驱动程序等待数据库返回的时间(秒),超过时间抛出异常unset
useGeneratedKeys(insert/update)允许MyBatis使用JDBC的getGeneratedKeys方法取出数据库内部生成的主键false
keyProperty(insert/update)MyBatis通过getGeneratedKeys的返回值或insert语句的selectKey子元素设置它的键值unset

sql

  sql标签可以用来定义可重用的SQL代码片段,可以包含在其他语句中。它可以被静态地参数化(可不指定),不同的实例拥有不同的属性值。一份sql可重用片段如下:

...
     <sql id="tmpSql" >
        select * from ${realdb}
    </sql>
...
    <select id="findByName" resultMap="UserResultMap">
        <include refid="tmpSql">
            <property name="realdb" value="user" />
        </include>
        where name=#{name}
    </select>
...

resultmap

  resultmap元素可以避免像JDBC那样需要从结果集中去处数据的代码。如下情况MyBatis会使用Javabean User来作为领域模型,MyBatis会默认在幕后自动创建一个ResutlMap,基于属性名来映射到JavaBean的属性上,如果列名没有精确匹配,也可以通过select as来指定别名来匹配, 如果User有nameAlias属性,则该属性会被赋name的返回值。

...
    <select id="findByName" resultType="com.xiaofan.test.User">
        select id, name as nameAlias, age from user where name=#{name}
    </select>
...

resultmap属性说明

resultmap
    - constructor : 构造方法注入,可不用暴露公共方法
    - id : 标记结果作为ID
    - result : 注入到字段或JavaBean属性的普通结果
    - association : 关联嵌套结果
    - collection : 集合嵌套结果
    - discriminator : 鉴别器,作用类似Java的Switch

    构造方法注入可在初始化时为类设置属性的值,不用暴露公共的方法,resultmap的constructor属性用法如下:

User类:
public class User {
...
    User(Long id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
...
}

xml映射文件:
<mapper namespace="com.xiaofan.test.UserDAO">
...
    <resultMap id="UserResultMap" type="User">
        <constructor>
            <idArg column="id" javaType="long" />
            <arg column="name" javaType="String" />
            <arg column="age" javaType="int" />
        </constructor>
    </resultMap>
    <select id="findByName" resultMap="UserResultMap">
        select * from user where name=#{name}
    </select>
...
</mapper>

五、动态SQL

  MyBatis的强大特性之一是它的动态SQL,它允许你根据不同条件拼接不同的SQL语句。MyBatis提供的动态SQL元素如下

- if - choose(when, otherwise) - trim(where, set) - foreach

if

if基本使用方法如下, 该语句可动态选择是否加入age筛选条件。

...
    <select id="findByName" resultMap="UserResultMap">
        select * from user where name=#{name}
        <if test="age != null" >
            and age = #{age}
        </if>
    </select>
...

choose(when, otherwise)

  当只想从条件中选择其中一个时,可以使用choose元素,类似Java中的Switch,多个条件匹配时返回第一个匹配的条件。使用范如下:

...
    <select id="findByName" resultMap="UserResultMap">
        select * from user where name = #{name}
        <choose>
            <when test="id != null">
                and id = #{id}
            </when>
            <otherwise>
                and age = #{age}
            </otherwise>
        </choose>
    </select>
...

trim(where, set)

  trim或where、set元素是用来解决条件元素匹配带来的多余连接符。其中where使用范例如下,他会自动去掉多余的and或or链接符号

...
    <select id="findByName" resultMap="UserResultMap">
        select * from user
        <where>
            <if test="name != null">
                and name = #{name}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </where>
    </select>
...

等同于:

...
    <select id="findByName" resultMap="UserResultMap">
        select * from user
        <trim prefix="where" prefixOverrides="and | or" >
            <if test="name != null">
                and name = #{name}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </trim>
    </select>
...

  set使用范例如下:

...
    <update id="update" parameterType="User">
        update user
        <set>
            <if test="name != null">
                name = #{name},
            </if>
            <if test="age != null">
                age =#{age},
            </if>
        </set>
        where id = #{id}
    </update>
...

等同于:

...
    <update id="update" parameterType="User">
        update user
        <trim prefix="set" suffixOverrides=",">
            <if test="name != null">
                name = #{name},
            </if>
            <if test="age != null">
                age =#{age},
            </if>
        </trim>
        where id = #{id}
    </update>
...

foreach

  foreach元素允许指定一个集合,并对该集合进行遍历。当使用可迭代对象或者数组时,index时但前迭代的次数,item时本次迭代的元素;当使用Map.Entry时,index是键,item是值。

...
    <insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
        insert into user(name, age) values
        <foreach item="item" index="idx" collection="list" separator="," >
            (#{item.name}, #{item.age})
        </foreach>
    </insert>
...

六、JAVA API使用

  MyBatis提供的主要Java接口是SqlSession,可以通过这个接口执行命令、获取映射器和管理事务。SqlSession是由SqlSessionFactory实例创建,SqlSessionFactory实例包含创建SqlSession实例的所有方法,SqlSessionFactory是由SqlSessionFactoryBuilder创建,SqlSessionFactoryBuilder可以通过XML配置来创建SqlSessionFactory。一个完整的调用示例如下:

public class Test {

    public static void main(String [] args) {

        try {

            String resource = "config.xml";
            Reader reader = Resources.getResourceAsReader(resource);
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(reader);
            SqlSession session = factory.openSession();
            UserDAO userDAO = session.getMapper(UserDAO.class);
            userDAO.batchInsert(Lists.<User>newArrayList(new User(null, "test1", 11), new User(null, "test2", 12)));
            session.commit();
            User user = userDAO.findByName(new User(null, "test1", null));
            System.out.println(JSON.toJSONString(user));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

配置文件读取

  默认情况下,MyBatis加载资源文件的默认路径是src/main, 如果需要指定其他路径,需在项目POM文件中指定资源文件路径。以上config.xml文件添加在resources资源文件夹下,因此需要将resources文件夹添加微资源加载路径,以保证config.xml文件能够被正确加载。

...
    <build>
        <finalName>xiaofantest</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>*.*</include>
                </includes>
            </resource>
        </resources>
    </build>
...

SqlSessionFactory

  SqlSessionFactory有六个方法可以创建SqlSession,其中使用较多的有两个,如下。默认openSession()方法没有参数,创建的SqlSession会开启一个事务,该事务需要用户自己提交。如果将创建参数autoCommit设置为‘true’,SqlSession则会开启自动提交功能。

SqlSession openSession()
SqlSession openSession(boolean autoCommit)

SqlSession

  SqlSession有超过20个方法,这些方法呗用来执行定义在SQL XML映射文件中的select、insert、update、delete语句,每一条语句都适用语句的ID属性和参数对象,参数可以是原声类型、JavaBean、POJO或Map。常用的方法如下:

// 带参数的方法
<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
// 不带参数的方法
<T> T selectOne(String statement)
<E> List<E> selectList(String statement)
<K,V> Map<K,V> selectMap(String statement, String mapKey)
int insert(String statement)
int update(String statement)
int delete(String statement)
// 查询方法的高级版本
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)

  对于如下一个sql 映射语句配置,有两种执行该sql的方式。一种是映射语句mapper的namespace+statementId; 另一种是指定mapper接口。

...
    <select id="findByName" resultMap="UserResultMap">
        select * from user
        <trim prefix="where" prefixOverrides="and | or" >
            <if test="name != null">
                and name = #{name}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </trim>
    </select>
...

  第一种方式示例:一个带参数的简单查询语句如下:

...
User user = session.selectOne("com.xiaofan.test.UserDAO.findByName", new User(null, "Jerry", null));
...

  第二种方式示例:指定mapper(映射器)对应的接口,每个映射器方法签名应该没有关联的字符串参数ID,但方法名必须与sql映射语句ID一致。

mapper接口定义:
public interface UserDAO {
...
    User findByName(User user);
...
}

Java方法调用:
...
    UserDAO userDAO = session.getMapper(UserDAO.class);
    User user = userDAO.findByName(new User(null, "test1", null));
...

映射器配置

  MyBatis sql映射器配置方式有两种,一种是写sql映射xml文件,并在MyBatis配置文件中添加mapper;另一种是在mapper接口中通过注解的方式配置sql映射语句。

  通过xml配置sql映射示例如下:

mapper接口定义:
public interface UserDAO {
...
    User findByName(User user);
...
}

sql映射文件:
<mapper namespace="com.xiaofan.test.UserDAO">
...
    <select id="findByName" resultMap="UserResultMap">
        select * from user
        <trim prefix="where" prefixOverrides="and | or" >
            <if test="name != null">
                and name = #{name}
            </if>
            <if test="age != null">
                and age = #{age}
            </if>
        </trim>
    </select>
...
</mapper>

MyBatis配置文件:
<configuration>
...
    <mappers>
        <mapper resource="user_mapper.xml"/>
    </mappers>
... 
</configuration>

  第二种方式,通过注解方式定义mapper接口示例如下, 更多注解说明见文档

Mapper接口类:
public interface UserDAO {
...
    @Select("select * from user where name = #{name}")
    User findByName(User user);
...
}

MyBatis配置文件:
<configuration>
...
    <mappers>
        <mapper class="com.xiaofan.test.UserDAO" />
    </mappers>
...    
</configuration>
    原文作者:SQL
    原文地址: https://juejin.im/entry/599a3d7b6fb9a02473218369
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞