一、单个参数且为简单类型参数
代码演示: StudentMapper 接口
public interface StudentMapper { /** * 当接口中的参数只有一个(单个参数),并且参数类型为简单类型 */ List<Student> selectById(Long id); List<Student> selectByName(String name); List<Student> selectByBirth(Date birth); List<Student> selectBySex(Character sex); }
StudentMapper.xml
<mapper namespace="com.powernode.mybatis.mapper.StudentMapper"> <select id="selectById" resultType="Student"> select * from t_student where id = #{id} </select> <select id="selectByName" resultType="Student"> select * from t_student where name = #{name} </select> <select id="selectByBirth" resultType="Student"> select * from t_student where birth = #{birth} </select> <select id="selectBySex" resultType="Student"> select * from t_student where sex = #{sex} </select> </mapper>
测试类
@Test public void testSimple() throws ParseException { SqlSession sqlSession = SqlSessionUtil.openSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // List<Student> students = mapper.selectById(1L); // List<Student> students = mapper.selectByName("张三"); // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // Date birth = sdf.parse("1990-02-01"); // List<Student> students = mapper.selectByBirth(birth); Character character = Character.valueOf('男'); List<Student> students = mapper.selectBySex(character); students.forEach(e -> System.out.println(e.toString())); sqlSession.close(); }
运行结果
19:21:28.479 default [main] DEBUG c.p.m.m.StudentMapper.selectBySex - ==> Preparing: select * from t_student where sex = ? 19:21:28.536 default [main] DEBUG c.p.m.m.StudentMapper.selectBySex - ==> Parameters: 男(String) 19:21:28.589 default [main] DEBUG c.p.m.m.StudentMapper.selectBySex - <== Total: 1 Student{id=1, name='张三', age=20, height=1.81, birth=Thu Feb 01 00:00:00 GMT+08:00 1990, sex=男}
补充一下: 1、<select id="selectById" resultType="Student" parameterType="java.lang.Long"> 可以不用加parameterType属性,mybatis会底层会自动找到对应的参数类型 2、parameterType属性的作用: 告诉mybatis框架,我这个方法的参数类型是什么类型。 mybatis框架自身带有类型自动推断机制,所以大部分情况下parameterType属性都是可以省略不写的。 SQL语句最终是这样的: select * from t student where id = ? JDBC代码是一定要给?传值的。 怎么传值?ps.setXxx(第几个问号,传什么值); ps.setLong(1,1L); ps.setstring(1,"zhangsan"); ps.setDate(1, new Date()); ps.setInt(1,100); ... mybatis底层到底调用setXxx的哪个方法,取决于parameterType属性的值。 注意:mybatis框架实际上内置了很多别名。可以参考开发手册。
二、参数为Map集合
代码演示: StudentMapper 接口
public interface StudentMapper { /** * Map集合参数 */ int insertStudentMap(Map<String,Object> map); }
StudentMapper.xml
<mapper namespace="com.powernode.mybatis.mapper.StudentMapper"> <insert id="insertStudentMap" parameterType="map"> insert into t_student values (null,#{name},#{age},#{height},#{birth},#{sex}) </insert> </mapper>
测试类
@Test public void testMap(){ SqlSession sqlSession = SqlSessionUtil.openSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); Map<String,Object> map = new HashMap<>(); map.put("name","小明"); map.put("age",23); map.put("birth",new Date()); map.put("height",1.7); map.put("sex","女"); int i = mapper.insertStudentMap(map); System.out.println(i); sqlSession.commit(); sqlSession.close(); }
三、参数为POJO类
代码演示: StudentMapper 接口
public interface StudentMapper { /** * Pojo 参数 */ int insertStudentPojo(Student student); }
StudentMapper.xml
<mapper namespace="com.powernode.mybatis.mapper.StudentMapper"> <insert id="insertStudentPojo" parameterType="Student"> insert into t_student values (null,#{name},#{age},#{height},#{birth},#{sex}) </insert> </mapper>
测试类
@Test public void testPojo(){ SqlSession sqlSession = SqlSessionUtil.openSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); Student student = new Student(); student.setName("小猪"); student.setAge(16); student.setHeight(1.9); student.setSex('女'); student.setBirth(new Date()); int i = mapper.insertStudentPojo(student); System.out.println(i); sqlSession.commit(); sqlSession.close(); }
四、多个参数
根据name和sex查询Student信息。 如果是多个参数的话,mybatis框架底层是怎么做的呢? mybatis框架会自动创建一个Map集合。并且Map集合是以这种方式存储参数的: map.put("arg0",name); map.put("arg1",sex); map.put("param1", name); map.put("param2", sex); 三种写法 <select id="selectByNameAndSex" resultType="student"> select * from t student where name = #{arge} and sex= #{arg1} select *from t student where name =#{param1} and sex=#{param2} select * from t_student where name = #{arg0} and sex = #{param2} </select> 代码演示: StudentMapper 接口
public interface StudentMapper { /** * 多个 参数: * mybatis会自动创建一个map集合, */ List<Student> selectByNameAndSex(String name,Character sex); }
StudentMapper.xml
<mapper namespace="com.powernode.mybatis.mapper.StudentMapper"> <select id="selectByNameAndSex" resultType="Student"> select * from t_student where name = #{arg0} and sex = #{arg1} </select> </mapper>
注意:这个arg0,arg1是mybatis底层框架自动生成的,必须怎么写 测试类
@Test public void testPojo(){ SqlSession sqlSession = SqlSessionUtil.openSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); Student student = new Student(); student.setName("小猪"); student.setAge(16); student.setHeight(1.9); student.setSex('女'); student.setBirth(new Date()); int i = mapper.insertStudentPojo(student); System.out.println(i); sqlSession.commit(); sqlSession.close(); }
运行结果
19:34:05.620 default [main] DEBUG c.p.m.m.S.selectByNameAndSex - ==> Preparing: select * from t_student where name = ? and sex = ? 19:34:05.704 default [main] DEBUG c.p.m.m.S.selectByNameAndSex - ==> Parameters: 小美(String), 女(String) 19:34:05.756 default [main] DEBUG c.p.m.m.S.selectByNameAndSex - <== Total: 1 Student{id=4, name='小美', age=16, height=1.9, birth=Sun Oct 23 00:00:00 GMT+08:00 2022, sex=女}
五、@Param注解
如果不想使用mybatis底层默认的参数,那么可以使用@Param注解来解决 代码演示: StudentMapper 接口
public interface StudentMapper { /** * 使用param注解,指定 多个 参数的名字 */ List<Student> selectByNameAndSex2(@Param("name") String name, @Param("sex") Character sex); }
StudentMapper.xml
<mapper namespace="com.powernode.mybatis.mapper.StudentMapper"> <select id="selectByNameAndSex2" resultType="Student"> select * from t_student where name = #{name} and sex = #{sex} </select> </mapper>
注意:这个arg0,arg1是mybatis底层框架自动生成的,必须这么写 测试类
@Test public void testParams(){ SqlSession sqlSession = SqlSessionUtil.openSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> students = mapper.selectByNameAndSex("小美", '女'); students.forEach(e-> System.out.println(e.toString())); sqlSession.close(); }
运行结果
19:34:05.620 default [main] DEBUG c.p.m.m.S.selectByNameAndSex - ==> Preparing: select * from t_student where name = ? and sex = ? 19:34:05.704 default [main] DEBUG c.p.m.m.S.selectByNameAndSex - ==> Parameters: 小美(String), 女(String) 19:34:05.756 default [main] DEBUG c.p.m.m.S.selectByNameAndSex - <== Total: 1 Student{id=4, name='小美', age=16, height=1.9, birth=Sun Oct 23 00:00:00 GMT+08:00 2022, sex=女}
六、补充知识点:结果映射
resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
之前你已经见过简单映射语句的示例,它们没有显式指定 resultMap。比如:
<select id="selectUsers" resultType="map"> select id, username, hashedPassword from some_table where id = #{id} </select>
上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。虽然在大部分情况下都够用,但是 HashMap 并不是一个很好的领域模型。你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 对象)作为领域模型。MyBatis 对两者都提供了支持。看看下面这个 JavaBean:
package com.someapp.model; public class User { private int id; private String username; private String hashedPassword; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getHashedPassword() { return hashedPassword; } public void setHashedPassword(String hashedPassword) { this.hashedPassword = hashedPassword; } }
基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些属性会对应到 select 语句中的列名。这样的一个 JavaBean 可以被映射到 ResultSet,就像映射到 HashMap 一样简单。
<select id="selectUsers" resultType="com.someapp.model.User"> select id, username, hashedPassword from some_table where id = #{id} </select>
类型别名是你的好帮手。使用它们,你就可以不用输入类的全限定名了。比如:
<!-- mybatis-config.xml 中 --> <typeAlias type="com.someapp.model.User" alias="User"/> <!-- SQL 映射 XML 中 --> <select id="selectUsers" resultType="User"> select id, username, hashedPassword from some_table where id = #{id} </select>
在这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上。如果列名和属性名不能匹配上,可以在 SELECT 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配。比如:
<select id="selectUsers" resultType="User"> select user_id as "id", user_name as "userName", hashed_password as "hashedPassword" from some_table where id = #{id} </select>
在学习了上面的知识后,你会发现上面的例子没有一个需要显式配置 ResultMap,这就是 ResultMap 的优秀之处——你完全可以不用显式地配置它们。 虽然上面的例子不用显式配置 ResultMap。 但为了讲解,我们来看看如果在刚刚的示例中,显式使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。
<resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="password" column="hashed_password"/> </resultMap>
然后在引用它的语句中设置 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:
<select id="selectUsers" resultMap="userResultMap"> select user_id, user_name, hashed_password from some_table where id = #{id} </select>
高级结果映射
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
比如,我们如何映射下面这个语句?
<!-- 非常复杂的语句 --> <select id="selectBlogDetails" resultMap="detailedBlogResultMap"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id} </select>
你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写,有很多的博文,每篇博文有零或多条的评论和标签。 我们先来看看下面这个完整的例子,它是一个非常复杂的结果映射(假设作者,博客,博文,评论和标签都是类型别名)。 不用紧张,我们会一步一步地来说明。虽然它看起来令人望而生畏,但其实非常简单。
<!-- 非常复杂的结果映射 --> <resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="blog_id" javaType="int"/> </constructor> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> <result property="favouriteSection" column="author_favourite_section"/> </association> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" javaType="Author"/> <collection property="comments" ofType="Comment"> <id property="id" column="comment_id"/> </collection> <collection property="tags" ofType="Tag" > <id property="id" column="tag_id"/> </collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> </collection> </resultMap>
resultMap 元素有很多子元素和一个值得深入探讨的结构。 下面是resultMap 元素的概念视图。
结果映射(resultMap)
constructor - 用于在实例化类时,注入结果到构造方法中
idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
arg - 将被注入到构造方法的一个普通结果
id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
result – 注入到字段或 JavaBean 属性的普通结果
association – 一个复杂类型的关联;许多结果将包装成这种类型
嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
collection – 一个复杂类型的集合
嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
discriminator – 使用结果值来决定使用哪个 resultMap
case – 基于某些值的结果映射
嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
ResultMap 的属性列表
最好逐步建立结果映射。单元测试可以在这个过程中起到很大帮助。 如果你尝试一次性创建像上面示例那么巨大的结果映射,不仅容易出错,难度也会直线上升。 所以,从最简单的形态开始,逐步迭代。而且别忘了单元测试! 有时候,框架的行为像是一个黑盒子(无论是否开源)。因此,为了确保实现的行为与你的期望相一致,最好编写单元测试。 并且单元测试在提交 bug 时也能起到很大的作用。
下一部分将详细说明每个元素。
id & result
这些元素是结果映射的基础。id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
两个元素都有一些属性:
Id 和 Result 的属性
支持的 JDBC 类型
为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。
构造方法
通过修改对象属性的方式,可以满足大多数的数据传输对象(Data Transfer Object, DTO)以及绝大部分领域模型的要求。但有些情况下你想使用不可变类。 一般来说,很少改变或基本不变的包含引用或数据的表,很适合使用不可变类。 构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBean 属性来完成注入,但有一些人更青睐于通过构造方法进行注入。 constructor 元素就是为此而生的。
看看下面这个构造方法:
public class User { //... public User(Integer id, String username, int age) { //... } //... }
为了将结果注入构造方法,MyBatis 需要通过某种方式定位相应的构造方法。 在下面的例子中,MyBatis 搜索一个声明了三个形参的的构造方法,参数类型以 java.lang.Integer, java.lang.String 和 int 的顺序给出。
<constructor> <idArg column="id" javaType="int"/> <arg column="username" javaType="String"/> <arg column="age" javaType="_int"/> </constructor>


