摘要
最近在研究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默认分配的库:
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 ">'关键字接受用户输入的方式可能导致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;
}
}
}