极简Mybatis之旅(一):CRUD

281 阅读3分钟
原文链接: click.aliyun.com

摘要

最近在研究mybatis框架,作为一个优秀ORM框架,mybatis很多优秀的设计思想值得借鉴。

mybatis的配置文件里,主要是config和mapper。config定义了全局参数:数据源类型(POOL, UNPOOLED, JNDI)、事务管理类型(默认为managed)、库url、账户信息和mapper文件路径。

config配置

<configuration>
    <environments default="classfly ">
        <environment id="classfly ">
            <transactionManager type="JDBC "/>
            <dataSource type="POOLED ">
                <property name="driver " value="com.mysql.jdbc.Driver " />
                <property name="url " value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8 " />
                <property name="username " value="yourusername " />
                <property name="password " value="yourpassword " />
            </dataSource>
        </environment>
    </environments>

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

DBNAME='test',是mybatis默认分配的库:
image

mapper配置

首先我们要问自己以下三个问题:

  • 为什么要配置mapper文件?
  • 怎么配置mapper文件?
  • mybatis如何解析mapper文件?

mapper文件的作用

mapper文件提供一种持久化层与应用层的"通信协议 ",通过mysql关键字resultMap, parameterType等维护应用代码DO对象与持久化存储数据之间的关联关系。

mapper文件里定义了select, insert, update, delete四种常用的DML语句,并定义statement={mapper namespace}.{operation}实现在应用代码层执行调用DB操作。

配置mapper文件

这里先给出测试用例用到的mapper配置文件的关键部分:

<!-- 代码清单-1 -->
<mapper namespace="com.classfly.mapper.UserMapper ">
    <resultMap id="user " type="pojo.User ">
        <result column="user_id " property="userId " />
        <result column="user_name " property="userName " />
        <result column="password " property="password " />
        <result column="age " property="age " />
    </resultMap>

    <select id="query " resultMap="user ">
        SELECT * FROM user;
    </select>

    <insert id="insert " parameterType="pojo.User ">
        insert into user
          (
            user_id,
            user_name,
            password,
            age)
        values
          (
            #{userId},
            #{userName},
            #{password},
            #{age}
          )
        <selectKey resultType="pojo.User " keyProperty="id " order="AFTER ">
            select 1313253 as id
        </selectKey>
    </insert>

    <update id="update " parameterType="pojo.User ">
        UPDATE user
        SET user_name = #{userName}
          , password = #{password}
          , age = #{age}
        WHERE user_id = #{userId}
    </update>

    <delete id="delete ">
        DELETE FROM user
        WHERE user_id = #{userId}
    </delete>
</mapper>

mapper namespace定义了DML语句的作用范围,那如果两个不同的mapper文件定义相同的namespace会发生什么?由于mysql保持namespace的全局唯一性,所以在解析mapper文件时mysql会抛异常提示开发者修改mapper文件以保持namespace的全局唯一性。

resultMap

resultMap关键字将代码和持久化层的数据映射抽象出来,用户无需关心两者之间如何映射。

我们可以使用HashMap完成代码和持久化层的数据映射关系,不但缺乏通用型且代码层需要将对象转换成map结构。resultMap接受HashMap结构和JavaBean或者POJO对象,提供轻量级的参数映射方案。

resultMap有"别名 "的功能,你无需每个DML都写一大串的映射语句,秉着"仅定义一次 "的原则,我们可以这么做:

<resultMap id="userResultMap " type="User ">
  <id property="id " column="user_id " />
  <result property="username " column="user_name "/>
  <result property="password " column="hashed_password "/>
</resultMap>

<select id="query " resultMap="user ">
        SELECT * FROM user
        ORDER BY ${id};
</select>

Now if only the world was always that simple!

'#'和'$'

从"代码清单-1 "里可以看出,占位符#和 分 别 实 现 了 P O J O 和 D B 数 据 的 映 射 关 系 以 及 非 转 义 的 串 。 用 ′ " role="presentation ">分别实现了POJO和DB数据的映射关系以及非转义的串。用' '关键字接受用户输入的方式可能导致sql注入攻击,不可取!

mybatis如何解析mapper文件

To be continued...

测试用例

/**
 * Created by fujianbo on 2018/4/22.
 *
 * @author fujianbo
 * @date 2018/04/22
 */
public class TestMybatis {
    @Test
    public void testQuery() {
        SqlSession sqlSession = buildMySqlEnv("config.xml ");
        List<User> userList = sqlSession.selectList("com.classfly.mapper.UserMapper.query ");
        for (User p : userList) {
            System.out.println(p);
        }
    }

    @Test
    public void testInsert() {
        SqlSession sqlSession = buildMySqlEnv("config.xml ");
        User user = new User();
        user.setUserId(124L);
        user.setAge(26);
        user.setPassword("test_123 ");
        user.setUserName("芸Rey ");
        if (sqlSession.insert("com.classfly.mapper.UserMapper.insert ", user) > 0) {
            sqlSession.commit();
        }
    }

    @Test
    public void testUpdate() {
        SqlSession sqlSession = buildMySqlEnv("config.xml ");
        User user = new User();
        user.setUserId(124L);
        user.setAge(26);
        user.setUserName("芸Rey ");
        user.setPassword("test_modified ");
        if (sqlSession.update("com.classfly.mapper.UserMapper.update ", user) > 0) {
            sqlSession.commit();
        }
    }

    @Test
    public void testDelete() {
        SqlSession sqlSession = buildMySqlEnv("config.xml ");
        User user = new User();
        user.setUserId(123L);
        if (sqlSession.update("com.classfly.mapper.UserMapper.delete ", user) > 0) {
            sqlSession.commit();
        }
    }

    private static SqlSession buildMySqlEnv(String resource) {
        try {
            return new SqlSessionFactoryBuilder()
                .build(org.apache.ibatis.io.Resources.getResourceAsStream(resource))
                .openSession();
        } catch (IOException e) {
            System.out.printf("Failed to build mysql environment! ");
            return null;
        }
    }
}

代码链接