概述
撸完Mybatis-02Mybatis XML方式概述及配置步骤之后,准备工作准备完毕,就可以开始学习具体的用法了。我们先来看下select元素
select用法
权限系统中,几个常见的业务,需要查询出系统中的用户、角色、权限等数据, 纯JDBC时,需要写查询语句,并且对结果集进行手工处理,将结果映射到对象的属性中。 而如果使用Mybatis,只需要在XML中添加一个select元素,写一个SQL,做一些简单的配置,就可以将结果集映射到对象中。
根据用户id查询用户信息
1.UserMapper接口中添加接口方法
package com.artisan.mybatis.xml.mapper; import com.artisan.mybatis.xml.domain.SysUser; public interface UserMapper { /** * * * @Title: selectSysUserById * * @Description: 通过ID查询系统用户 * * @param id * @return * * @return: SysUser */ SysUser selectSysUserById(Long id); }
因为ID是主键,所以只可能查询出一个SysUser ,所以selectSysUserById直接返回SysUser对象即可。
2. UserMapper.xml中配置resultMap和select元素
首先查看下数据库SysUser的字段
再来看下SysUser实体类中的属性
数据库字段和实体类中的属性名称不一致,两种方式
- 配置resultMap 映射 (selectSysUserById演示)
- 通过别名的方式 (selectAll 演示)
配置之前,先来探讨一个问题: 前面创建接口和XML时提到过,接口和XML是通过将namespace设置为接口的全限定名来进行关联的,那么接口中的方法和XML又是怎么关联的呢?
Mybatis 是通过 xml中的select标签的ID和接口名称一致这种方式将接口方法和XML中定义的SQL语句关联起来的,如果接口方法中没有和XML 中的id属性相对应的值,启动程序便会报错。
映射XML和接口的命名需要符合如下规范:
- 当只是用XML而不适用接口的时候,namespace的值可以设置为任意不重复的名称
- 标签的id属性在任何时候都不能出现英文句号,并且同一个命名空间下不能出现重复的id
- 因为接口方法是可以重载的,所以接口中可以出现多个同名但参数不同的方法,但是xml中的id不能重复。
搞清了这些,我们来配置UserMapper.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接口和XML文件关联的时候, namespace的值就需要配置成接口的全限定名称 --> <mapper namespace="com.artisan.mybatis.xml.mapper.UserMapper"> <!-- 通过resultMap标签配置Java对象和查询结果列的对应关系 --> <resultMap id="userMap" type="com.artisan.mybatis.xml.domain.SysUser"> <id column="id" property="id" /> <result property="userName" column="user_name" /> <result property="userPassword" column="user_password" /> <result property="userEmail" column="user_email" /> <result property="userInfo" column="user_info" /> <result property="headImg" column="head_img" jdbcType="BLOB" /> <result property="createTime" column="create_time" jdbcType="TIMESTAMP" /> </resultMap> <!-- 通过ID查询系统用户 --> <select id="selectSysUserById" parameterType="Long" resultMap="userMap"> select a.id, a.user_name, a.user_password, a.user_email, a.user_info, a.head_img, a.create_time from sys_user a where id = #{id} </select> </mapper>
解释下XML中的一些标签和属性的作用
<select>
:映射查询语句使用的标签- id : 命名空间的唯一标示,代表这个语句
- resultMap:用于设置返回值的类型和映射关系
- #{id}: Mybatis SQL中使用预编译参数的一种方式,大括号中的id是传入的参数名。 表示使用preparedstatement设置占位符号并将输入变量id传到sql。
下面看下接口方法的返回值要如何定义
接口中定义的返回值类型必须和XML中配置的ResultType类型一致,否则就会因为类型不一致而抛出异常。 返回值类型是有XML中的resultType(或者resultMap中的type)决定的,不是由接口中所写的返回值类型决定的(这里我们建的是XML方式,先忽略注解的情况)
查询全部的SysUser
添加接口方法
/** * * * @Title: selectAll * * @Description: 查询全部的SysUser * * @return * * @return: List<SysUser> */ List<SysUser> selectAll();
配置UserMapper.xml
<!-- 通过全部的系统用户 --> <select id="selectAll" resultType="com.artisan.mybatis.xml.domain.SysUser"> select a.id id, a.user_name userName, a.user_password userPassword, a.user_email userEmail, a.user_info userInfo, a.head_img headImg, a.create_time createTime from sys_user a </select>
我们看到这个接口中对应方法的返回类型为 List<SysUser> , 为什么不是SysUser呢?
当返回值最多只有1个结果的时候(可以是0个),可以将接口返回值定义为SysUser,而不是List<SysUser>。
当执行的SQL返回多个结果时,必须使用List<SysUser> 或者SysUser[] 作为返回值,如果使用SysUser,会抛出TooManyResultsException异常。
观察一下UserMapper.xml中的selectSysUserById和selectAll的区别
selectSysUserById使用了resultMap来设置结果映射,而selectAll使用resultType直接指定了返回结果的类型。 可以发现,如果使用resultType来设置返回结果的类型,需要在SQL中为所有列名和属性名不一致的列设置别名,通过设置别名使最终的查询结果和resultType指定对象的属性名保持一致,进而实现自动映射.
单元测试
基础测试类BaseMapperTest
这个类用于被继承,主要功能是加载配置文件,提供获取SqlSession的方法
在src/test/java中创建包 com.artisan.mybatis.xml.mapper
package com.artisan.mybatis.xml.mapper; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.log4j.Logger; import org.junit.BeforeClass; public class BaseMapperTest { private static Logger logger = Logger.getLogger(BaseMapperTest.class); private static SqlSessionFactory sessionFactory; @BeforeClass public static void init() { try { // 根据mybatis的配置文件创建sqlSessionFactory String config = "mybatis-config.xml"; Reader reader = Resources.getResourceAsReader(config); sessionFactory = new SqlSessionFactoryBuilder().build(reader); logger.info("sessionFactory bulit successfully"); reader.close(); logger.info("reader close successfully"); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession() { return sessionFactory.openSession(); } }
package com.artisan.mybatis.xml.mapper; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Test; import com.artisan.mybatis.xml.domain.SysUser; /** * * * @ClassName: UserMapperTest * * @Description: UserMapperTest 单元测试类 * * @author: Mr.Yang * * @date: 2018年4月14日 下午1:59:31 */ public class UserMapperTest extends BaseMapperTest { private static Logger logger = Logger.getLogger(UserMapperTest.class); @Test public void selectSysUserByIdTest() { logger.info("selectSysUserByIdTest"); // 获取SqlSession SqlSession sqlSession = getSqlSession(); try { // 获取UserMapper接口 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用selectSysUserById方法,查询id=1的用户 SysUser sysUser = userMapper.selectSysUserById((long) 1); // sysUser不为空 Assert.assertNotNull(sysUser); // userName = "admin" Assert.assertEquals("admin", sysUser.getUserName()); logger.info(sysUser); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); logger.info("sqlSession close successfully "); } } @Test public void selectAllTest() { logger.info("selectAllTest"); // 获取SqlSession SqlSession sqlSession = getSqlSession(); try { // 获取UserMapper接口 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用selectAll,查询全部用户 List<SysUser> userList = userMapper.selectAll(); // 结果不为空 Assert.assertNotNull(userList); // 结果大于0 Assert.assertTrue(userList.size() > 0); logger.info("userList总数为:" + userList.size()); for (SysUser sysUser : userList) { logger.info(sysUser); } } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); logger.info("sqlSession close successfully "); } } }
输出日志:
2018-04-15 14:11:47,623 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-04-15 14:11:47,627 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-04-15 14:11:47,630 INFO [main] (UserMapperTest.java:29) - selectSysUserByIdTest 2018-04-15 14:11:48,258 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Preparing: select a.id, a.user_name, a.user_password, a.user_email, a.user_info, a.head_img, a.create_time from sys_user a where id = ? 2018-04-15 14:11:48,347 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 1(Long) 2018-04-15 14:11:48,379 TRACE [main] (BaseJdbcLogger.java:148) - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time 2018-04-15 14:11:48,380 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 1, admin, 123456, admin@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0 2018-04-15 14:11:48,385 DEBUG [main] (BaseJdbcLogger.java:142) - <== Total: 1 2018-04-15 14:11:48,386 INFO [main] (UserMapperTest.java:41) - SysUser [id=1, userName=admin, userPassword=123456, userEmail=admin@artisan.com, userInfo=管理员用户, headImg=[18, 49, 35, 18, 48], createTime=Fri Apr 13 21:12:47 BOT 2018] 2018-04-15 14:11:48,392 INFO [main] (UserMapperTest.java:46) - sqlSession close successfully 2018-04-15 14:11:48,393 INFO [main] (UserMapperTest.java:52) - selectAllTest 2018-04-15 14:11:48,402 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Preparing: select a.id id, a.user_name userName, a.user_password userPassword, a.user_email userEmail, a.user_info userInfo, a.head_img headImg, a.create_time createTime from sys_user a 2018-04-15 14:11:48,403 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 2018-04-15 14:11:48,404 TRACE [main] (BaseJdbcLogger.java:148) - <== Columns: id, userName, userPassword, userEmail, userInfo, headImg, createTime 2018-04-15 14:11:48,405 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 1, admin, 123456, admin@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0 2018-04-15 14:11:48,407 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 1001, test, 123456, test@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0 2018-04-15 14:11:48,412 DEBUG [main] (BaseJdbcLogger.java:142) - <== Total: 2 2018-04-15 14:11:48,412 INFO [main] (UserMapperTest.java:65) - userList总数为:2 2018-04-15 14:11:48,413 INFO [main] (UserMapperTest.java:67) - SysUser [id=1, userName=admin, userPassword=123456, userEmail=admin@artisan.com, userInfo=管理员用户, headImg=[18, 49, 35, 18, 48], createTime=Fri Apr 13 21:12:47 BOT 2018] 2018-04-15 14:11:48,413 INFO [main] (UserMapperTest.java:67) - SysUser [id=1001, userName=test, userPassword=123456, userEmail=test@artisan.com, userInfo=测试用户, headImg=[18, 49, 35, 18, 48], createTime=Fri Apr 13 21:12:47 BOT 2018] 2018-04-15 14:11:48,414 INFO [main] (UserMapperTest.java:73) - sqlSession close successfully
全局属性mapUnderscoreToCamelCase
在数据库中,由于大部分数据库设置不区分大小写,一次下画线方式的命名很常见,比如user_name, user_email . 在Java中,一般都是用驼峰式命名,比如userName,userEmail
因为数据库和Java中的这两种命名方式很常见,为此Mybatis提供了一个全局属性mapUnderscoreToCamelCase,通过配置这个属性为true可以自动将下画线方式命名的数据库列映射到java对象驼峰式命名属性中。 这个属性默认为false,若想使用,需要在mybatis全局配置文件中settings节点下增加如下配置
<!-- 通过配置这个属性为true可以自动将下画线方式命名的数据库列映射到java对象驼峰式命名属性中 --> <setting name="mapUnderscoreToCamelCase" value="true"/>
1.增加UserMapper接口
// 仅仅是为了测试mapUnderscoreToCamelCase 全局属性 SysUser selectUserByIdUseMapUnderscoreToCamelCase(Long id);
2.配置UserMapper.xml
<!-- 通过ID查询系统用户 ,配合mapUnderscoreToCamelCase设置为true --> <select id="selectUserByIdUseMapUnderscoreToCamelCase" parameterType="Long" resultType="com.artisan.mybatis.xml.domain.SysUser"> select a.id, a.user_name, a.user_password, a.user_email, a.user_info, a.head_img, a.create_time from sys_user a where id = #{id} </select>
注意使用的是resultType ,而不是resutlMap
3.单元测试
@Test public void selectUserByIdUseMapUnderscoreToCamelCaseTest() { logger.info("selectUserByIdUseMapUnderscoreToCamelCaseTest"); // 获取SqlSession SqlSession sqlSession = getSqlSession(); try { // 获取UserMapper接口 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用selectSysUserById方法,查询id=1的用户 SysUser sysUser = userMapper.selectUserByIdUseMapUnderscoreToCamelCase((long) 1001); // sysUser不为空 Assert.assertNotNull(sysUser); // userName = "admin" Assert.assertEquals("test", sysUser.getUserName()); logger.info(sysUser); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); logger.info("sqlSession close successfully "); } }
日志
2018-04-15 14:23:39,196 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-04-15 14:23:39,200 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-04-15 14:23:39,204 INFO [main] (UserMapperTest.java:79) - selectUserByIdUseMapUnderscoreToCamelCaseTest 2018-04-15 14:23:39,802 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Preparing: select a.id, a.user_name, a.user_password, a.user_email, a.user_info, a.head_img, a.create_time from sys_user a where id = ? 2018-04-15 14:23:39,896 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 1001(Long) 2018-04-15 14:23:39,932 TRACE [main] (BaseJdbcLogger.java:148) - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time 2018-04-15 14:23:39,933 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 1001, test, 123456, test@artisan.com, <<BLOB>>, <<BLOB>>, 2018-04-13 21:12:47.0 2018-04-15 14:23:39,936 DEBUG [main] (BaseJdbcLogger.java:142) - <== Total: 1 2018-04-15 14:23:39,939 INFO [main] (UserMapperTest.java:96) - sqlSession close successfully
多表关联查询
需求一: 根据用户id返回该用户所拥有的角色,返回的结果为角色集合,结果只有角色的信息即可,不用包含其他字段信息。
根据我们的数据模型可知,涉及到3个表, sys_user 、sys_role 、sys_user_role 。
1.UserMapper接口增加接口方法
/** * * * @Title: selectRoleByUserId * * @Description: 根据用户ID查询用户角色 * * @param userId * @return * * @return: List<SysRole> */ List<SysRole> selectRoleListByUserId(Long userId);
2. UserMapper.xml 配置语句
<!-- 根据用户ID查询用户角色 --> <select id="selectRoleListByUserId" parameterType="Long" resultType="com.artisan.mybatis.xml.domain.SysRole"> SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime FROM sys_user a JOIN sys_user_role b ON a.id = b.user_id JOIN sys_role c ON b.role_id = c.id WHERE a.id = #{userId} </select>
3. 单元测试
@Test public void selectRoleListByUserIdTest() { logger.info("selectRoleListByUserIdTest"); // 获取SqlSession SqlSession sqlSession = getSqlSession(); try { // 获取UserMapper接口 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用selectRoleListByUserId方法,查询用户id=1的角色 List<SysRole> roleList = userMapper.selectRoleListByUserId((long) 1); // roleList不为空 Assert.assertNotNull(roleList); // roleList > 0 Assert.assertTrue(roleList.size() > 0); logger.info(roleList); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); logger.info("sqlSession close successfully "); } }
选中方法名,运行单元测试
2018-04-15 15:45:39,368 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-04-15 15:45:39,373 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-04-15 15:45:39,377 INFO [main] (UserMapperTest.java:103) - selectRoleListByUserIdTest 2018-04-15 15:45:40,086 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Preparing: SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime FROM sys_user a JOIN sys_user_role b ON a.id = b.user_id JOIN sys_role c ON b.role_id = c.id WHERE a.id = ? 2018-04-15 15:45:40,212 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 1(Long) 2018-04-15 15:45:40,262 TRACE [main] (BaseJdbcLogger.java:148) - <== Columns: id, roleName, enabled, createBy, createTime 2018-04-15 15:45:40,263 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 1, 管理员, 1, 1, 2018-04-13 21:12:46.0 2018-04-15 15:45:40,269 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 2, 普通用户, 1, 1, 2018-04-13 21:12:46.0 2018-04-15 15:45:40,270 DEBUG [main] (BaseJdbcLogger.java:142) - <== Total: 2 2018-04-15 15:45:40,271 INFO [main] (UserMapperTest.java:116) - [SysRole [id=1, roleName=管理员, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null], SysRole [id=2, roleName=普通用户, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null]] 2018-04-15 15:45:40,287 INFO [main] (UserMapperTest.java:121) - sqlSession close successfully
符合数据库中的记录 。
需求二:假设有个需求(仅仅为了说明用法):以需求一为基础,不仅要包含sys_role的信息,同时还要包含当前用户的部分信息(不考虑嵌套的情况)
比如增加查询列 user_name , user_email 这个时候该如何设置resultType呢?
两种简单的方法
第一种方法是在SysRole对象中直接添加userName,userEmail属性,这样仍然使用SysRole作为返回值
第二种方法创建一个子类继承SysRole,将userName,userEmail作为子类的属性,将resultType的返回对象设置为子类 。 (这种方式适合在需要少量的额外字段时使用,如果需要其他表中大量列的时候就不合适了。)
在不考虑嵌套XML配置的情况下,我们来演示下第一种方法
1.SysRole添加 SysUser属性
/** * 用户信息 */ private SysUser user; setter/getter
2.UserMapper.xml
增加
<!-- 根据用户ID查询用户角色 ,增加sys_user中的部分字段 --> <select id="selectRoleListByUserIdMoreInfo" parameterType="Long" resultType="com.artisan.mybatis.xml.domain.SysRole"> SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime, a.user_name as 'user.userName', a.user_email as 'user.userEmail' FROM sys_user a JOIN sys_user_role b ON a.id = b.user_id JOIN sys_role c ON b.role_id = c.id WHERE a.id = #{userId} </select>
注意看查询列增加的两行
a.user_name as 'user.userName', a.user_email as 'user.userEmail'
这里设置别名的时候,使用的是“user.属性名”,user是SysRole中刚刚增加的属性,userName和userEmail是SysUser对象的属性,通过这种方式可以直接将值赋给user字段中的属性
3.单元测试
@Test public void selectRoleListByUserIdMoreInfoTest() { logger.info("selectRoleListByUserIdMoreInfoTest"); // 获取SqlSession SqlSession sqlSession = getSqlSession(); try { // 获取UserMapper接口 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 调用selectRoleListByUserId方法,查询用户id=1的角色 List<SysRole> roleList = userMapper.selectRoleListByUserIdMoreInfo((long) 1); // roleList不为空 Assert.assertNotNull(roleList); // roleList > 0 Assert.assertTrue(roleList.size() > 0); logger.info(roleList); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); logger.info("sqlSession close successfully "); } }
日志
2018-04-15 17:56:06,453 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-04-15 17:56:06,457 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-04-15 17:56:06,460 INFO [main] (UserMapperTest.java:127) - selectRoleListByUserIdMoreInfoTest 2018-04-15 17:56:06,987 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Preparing: SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime, a.user_name as 'user.userName', a.user_email as 'user.userEmail' FROM sys_user a JOIN sys_user_role b ON a.id = b.user_id JOIN sys_role c ON b.role_id = c.id WHERE a.id = ? 2018-04-15 17:56:07,085 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 1(Long) 2018-04-15 17:56:07,119 TRACE [main] (BaseJdbcLogger.java:148) - <== Columns: id, roleName, enabled, createBy, createTime, user.userName, user.userEmail 2018-04-15 17:56:07,119 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 1, 管理员, 1, 1, 2018-04-13 21:12:46.0, admin, admin@artisan.com 2018-04-15 17:56:07,124 TRACE [main] (BaseJdbcLogger.java:148) - <== Row: 2, 普通用户, 1, 1, 2018-04-13 21:12:46.0, admin, admin@artisan.com 2018-04-15 17:56:07,126 DEBUG [main] (BaseJdbcLogger.java:142) - <== Total: 2 2018-04-15 17:56:07,126 INFO [main] (UserMapperTest.java:140) - [SysRole [id=1, roleName=管理员, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=SysUser [id=null, userName=admin, userPassword=null, userEmail=admin@artisan.com, userInfo=null, headImg=null, createTime=null], privilegeList=null], SysRole [id=2, roleName=普通用户, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=SysUser [id=null, userName=admin, userPassword=null, userEmail=admin@artisan.com, userInfo=null, headImg=null, createTime=null], privilegeList=null]] 2018-04-15 17:56:07,133 INFO [main] (UserMapperTest.java:145) - sqlSession close successfully
为了更加直观,我们在Assert.assertNotNull(roleList); 加上断点,调测一下,如下图所示
小结
Select元素的用法总结到此,接下来我们来开启insert元素的用法