mapper映射文档是Mybatis的核心所在,对数据库的操作都在里头了。
在mapper映射文档中,以<mapper/>
作为根节点,其下可以有的子节点分别是:
1 | select, insert, update, delete, cache, cache-ref, resultMap, parameterMap, sql |
本节就来介绍这些子节点。
先来看看 insert
insert, update, delete三个标签的属性和使用方法相似,这里就以insert为例介绍。
123456789101112131415161718192021222324 | <?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" > <!-- namespace 的值对应一个DAO --> <mapper namespace="com.wzt.batis.dao.UserMapper"> <!-- 1. id 对应DAO中的一个方法 --> <insert id="insert" <!-- 2. parameterType 将要传入语句的参数的完全限定类名或别名 这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数 --> parameterType="user" <!-- 3. parameterMap 可以通过额外的标签来对入参进行更复杂的操作 --> parameterMap="uuu" <!-- 4. useGeneratedKeys 这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键 5. keyProperty 将获得的主键赋值给指定的Java类中的属性,如此处的id 6. keyColumn 从数据库表的id列取主键值,数据库设定了主键则不需要显式配置 --> useGeneratedKeys="true" keyProperty="id" keyColumn="id" <!-- 7. flushCache 任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认为true--> flushCache="true" <!-- 8. statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。--> statementType="PREPARED" <!-- 9. timeout 这个设置是在抛出异常之前,驱动进程等待数据库返回请求结果的秒数。--> timeout="20"/> <parameterMap id="uuu" type="user"/> </mapper> |
代码见真章:
数据库表
1234567 | CREATE TABLE IF NOT EXISTS `tb_user` ( `id` INT COLLATE utf8_bin NOT NULL AUTO_INCREMENT COMMENT '编号', `name` VARCHAR (36) COLLATE utf8_bin NOT NULL COMMENT '姓名', `age` INT NOT NULL COMMENT '年龄', `gender` CHAR (10) COLLATE utf8_bin NOT NULL COMMENT '性别', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin |
DO 类
省略getter/setter方法:
123456 | public class UserDo { private Integer id; private String name; private Integer age; private String gender; } |
Mapper 接口
1234 | @Mapper public interface UserMapper { Integer insert(UserDo user); } |
Mapper 映射文档
123456789 | <?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.wzt.batis.dao.UserMapper"> <insert id="insert" parameterType="user"> INSERT INTO tb_user (id,name,age,gender)VALUES ( #{id},#{name},#{age},#{gender} ) </insert> </mapper> |
测试类
1234567891011121314151617181920212223 | public class TestMain { public static void main(String[] args) { SqlSession sqlSession = getSessionFactory().openSession(true); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); UserDo user = new UserDo(1,"LiLei",12,"male"); Integer res = userMapper.insert(user); } /** * Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与数据库进行交互 */ private static SqlSessionFactory getSessionFactory() { SqlSessionFactory sessionFactory = null; String resource = "mybatis-config.xml"; try { sessionFactory = new SqlSessionFactoryBuilder() .build(Resources.getResourceAsReader(resource)); } catch (IOException e) { e.printStackTrace(); } return sessionFactory; } } |
执行后数据库的 tb_user 表中多了一条数据。
userMapper.insert(user)
成功执行返回1(失败则为0)。
如果插入时不指定id,采用数据库的自增主键,并想要得到记录的id值,可以这样修改:
12345 | <insert id="insert" parameterType="user" useGeneratedKeys="true" keyProperty="id"> INSERT INTO tb_user (name,age,gender)VALUES ( #{name},#{age},#{gender} ) </insert> |
或者使用 <selectKey/>
标签:
12345678 | <insert id="insert1" parameterType="UseR"> <selectKey keyProperty="id" resultType="int" order="AFTER" > SELECT LAST_INSERT_ID() </selectKey> INSERT INTO tb_user (name,age,gender)VALUES ( #{name},#{age},#{gender} ) </insert> |
如此,在userMapper.insert(user)
后便可从user
对象中取得id了。
注意: 数据库的自增id赋值给了keyProperty
指定的属性,insert
方法的返回值仍然是1或0。
这里用到了 <selectKey/>
标签,它可以返回记录在数据库中的id,看上去和<insert/>
标签的属性useGeneratedKeys
和keyProperty
功能重复了。
那么除了这个作用外,<selectKey/>
还有什么作用呢?
我们知道,MySQL支持主键自增,但是其它的数据库(如oracle)不支持啊;此外如果主键是UUID的,自增也无能为力了。此时<selectKey/>
就能派上用场了。
先将DO类的id改成String类型,然后mapper映射文档变成这样:
12345678910 | <insert id="insert" parameterType="user"> <!-- order变成了BEFORE,代表在insert前执行 --> <selectKey keyProperty="id" resultType="string" order="BEFORE" > SELECT UUID() </selectKey> <!-- 这里也加上了id列 --> INSERT INTO tb_user (id, name, age, gender)VALUES ( #{id}, #{name}, #{age}, #{gender} ) </insert> |
这样就会在insert语句前先执行selectKey,将UUID赋给id,然后再插入数据库。
不过在网上看到这里有个隐患,就是通过mybatis生成的UUID可能存在重复,emmm,所以实际工程中还是用java的UUID,更加方便,还不重复!
重头戏 select
先来看看mapper映射文档中,select 标签可以有哪些属性:
12345678 | <!-- 大多属性和insert相似,下面列几个多出来的 --> <select id="select" <!-- 下面两个二选一,用来指定返回的类型 --> resultType="user" resultMap="uuu" <!-- 尝试影响驱动进程每次批量返回的结果行数和这个设置值相等 --> fetchSize="10" <!-- 设置本条语句的结果是否被二级缓存,默认为true --> us 大专栏 Mybatis映射文档介绍eCache="true" /> |
要更全的可以查看相应的官方文档。
现在来看看select的demo。以上面的tb_user表为例,查询某个id的user记录的select为:
123 | <select id="findUserById" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE `id` = #{userId} </select> |
DAO层接口为:
1234 | @Mapper public interface UserMapper { UserDo findUserById(String userId); } |
可以看到select语句中的#{userId}
来自接口方法的入参,返回类型resultType
为user(这是个别名)。
这里有个注意点: 入参只有一个时,可以直接这么取;若是有多个,则需要加注解 @Param()
了,比如要根据性别和年龄来查:
12345 | @Mapper public interface UserMapper { UserDo findUserByGenderAndAge(@Param("gender") String gender, @Param("age") String age); } |
1234 | <select id="findUserByGenderAndAge" parameterType="string" resultType="user"> SELECT * FROM tb_user WHERE `gender` = #{gender} AND `age` = #{age} </select> |
在上面介绍select的属性时提到,返回类型可以用两个属性来指定,resultType
和 resultMap
,上面的demo用到了resultType
,下面讲讲resultMap
的用法。
什么都能转 resultMap
resultMap 最简单的用法就是字段的映射。 若数据库中字段和用来接收的Javabean的属性一致时,用resultType即可,但若是不一致,resultMap就派上用场了。 比如说数据库中的字段是uuid,Javabean中是id,此时需要这样配置:
123 | <resultMap id="userMap" type="user"> <result property="id" column="uuid"/> </resultMap> |
这样就完成了一个简单地字段映射。不过resultMap的强大之处远不止此。
在上面的demo中,返回接收的user的属性都是简单的基本类型的包装类型和String,但如果是个自定义的类,resultMap也可以处理得很好:
现在我们给User类中增加一个Role属性
1234 | public class UserDo { // ... private RoleDo role; } |
在数据库中增加两张表,分别是角色表tb_role,和用户角色关联表tb_user_role_relation:
123456789101112131415 | -- 角色表 CREATE TABLE IF NOT EXISTS tb_role ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '编号', `name` VARCHAR (20) NOT NULL COMMENT '角色名', PRIMARY KEY (`id`) ) ENGINE = INNODB DEFAULT CHARSET = utf8 COLLATE = utf8_bin; -- 用户角色关系表 CREATE TABLE IF NOT EXISTS tb_user_role_relation ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '编号', `user_id` VARCHAR (20) NOT NULL COMMENT '用户id', `role_id` VARCHAR (20) NOT NULL COMMENT '角色id', PRIMARY KEY (`id`) ) ENGINE = INNODB DEFAULT CHARSET = utf8 COLLATE = utf8_bin; |
在mapper映射文档中,可以用 <association/>
进行关联:
123456789101112131415 | <select id="select" resultMap="userMap" > SELECT u.*, r.`id` AS role_id, r.`name` AS role_name FROM tb_user u LEFT JOIN tb_user_role_relation urr ON u.id = urr.user_id LEFT JOIN tb_role r ON urr.role_id = r.id </select> <resultMap id="userMap" type="user"> <id property="id" column="id"/> <result property="age" column="age"/> <result property="gender" column="gender"/> <result property="name" column="name"/> <association property="role" javaType="role"> <id property="id" column="role_id"/> <result property="name" column="role_name"/> </association> </resultMap> |
便可将role和user关联起来:
12345678910 | { "id": "49", "name": "ZhangSan", "age": 25, "gender": "male", "role": { "id": 2, "name": "manager" } } |
在上面这个例子中,一个user只有一个role,但实际上一个user可能对应着多个role,在Javabean中的表现就是:
1234 | public class UserDo { // ... private List<RoleDo> roles; } |
此时可以用 <collection/>
进行关联:
12345678910 | <resultMap id="userMap" type="user"> <id property="id" column="id"/> <result property="age" column="age"/> <result property="gender" column="gender"/> <result property="name" column="name"/> <collection property="roles" ofType="role"> <id property="id" column="role_id"/> <result property="name" column="role_name"/> </collection> </resultMap> |
可以得到结果:
12345678910111213141516 | { "id": "49", "name": "ZhangSan", "age": 25, "gender": "male", "roles": [ { "id": 1, "name": "admin" }, { "id": 2, "name": "manager" } ] } |
到此,mapper映射文档介绍告一段落。