2.2、MyBatis的XML基本用法——select用法
2.2、MyBatis的XML基本用法——select用法
select用法
使用纯粹的JDBC时,需要写查询语句,并且对结果集进行手动处理,将结果映射到对象的属性中
使用 MyBatis 时,只需要在XML中添加一个select元素,写一个SQL,再做一些简单的配置,就可以将查询的结果直接映射到对象中
下面以用id查找用户为例,讲解select用法
1、添加对应接口方法selectById
public interface UserMapper { /** * 通过 id 查询用户 * * @param id * @return */ SysUser selectById(Long id); }
2、添加对应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 namespace="tk.mybatis.simple.mapper.UserMapper"> <resultMap id="userMap" type="tk.mybatis.simple.model.SysUser"> <id property="id" column="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> <select id="selectById" resultMap="userMap"> select * from sys_user where id = #{id} </select> </mapper>
接口与XML的关联:通过XML的<mapper>标签的namespace值设置为接口的全限定名称进行关联
接口中方法与XML的关联:通过XML的<select>标签的id值设置为接口方法名称进行关联
3、XML的设计规则:
a、只使用XML而不使用接口时,namespace的值可以设置为任意不重复的值
b、标签的 id 属性值在任何时候都不能出现英文句号 “.” ,并且同一个命名空间下不能出现重复的id
c、因为接口方法是可以重载的,所以接口中可以出现多个同名但参数不同的方法,但是 XML 中 id 的值不能重复,因而接口中的所有同名方法会对应着 XML 中的同一个 id 的方法。最常见的用法就是,同名方法中其中一个方法增加一个 RowBound 类型的参数用于实现分页查询
4、XML的标签与属性
<select>标签:映射查询语句使用的标签
id:命名空间中的唯一标识符,可用来代表这条语句
resultMap:用于设置返回值的类型和映射关系
resultType:直接自动影射为某对象
select * from sys_user where id=#{id} 是查询语句
#{id}:MyBatis SQL中使用预编译参数的一种方式,大括号中的 id 是传入的参数名
在上面的select中,使用resultMap设置返回值的类型,这里的userMap就是上面<resultMap>中的id属性值,通过id引用需要的<resultMap>
<resultMap>标签:用于配置Java对象的属性和查询结果列的对应关系,通过resultMap中配置的column和property可以将查询列的值映射到type对象的属性上,因此当我们使用select*查询所有列的时候,MyBatis也可以将结果正确地映射到SysUser对象上
id:必填,并且唯一。在select标签中,resultMap指定的值即为此处id所设置的值
type:必填,用于配置查询列所映射到的Java对象类型
extends:选填,可以配置当前的resultMap继承自其他的resultMap,属性值为继承resultMap的id
autoMapping:选填,可选值为true或false,用于是否启用非映射字段(没有在resultMap中的字段)的自动映射功能,该可以覆盖全局的autoMappingBehavior
<resultMap>下的标签:
<constructor>标签:配置使用构造方法注入结果,包含以下两个子标签
<idArg>标签:id参数,标记结果作为id(唯一值),可以帮助提高整体性能。
<arg>标签:注入到构造方法的一个普通结果。
<id>标签:一个id结果,标记结果作为id(唯一值),可以帮助提高整体性能。
<result>标签:注入到Java对象属性的普通结果。
<idArg>、<arg>和<id>、<result>的含义相同,不同在于前者是通过类的构造函数注入,后者是通过属性的setter方法注入
<association>标签:一个复杂的类型关联,许多结果将包成这种类型。
<collection>标签:复杂类型的集合。
<discriminator>标签:根据结果值来决定使用哪个结果映射。
<case>标签:基于某些值的结果映射。
<id>和<result>里面的属性
column:从数据库中得到的列名,或者是列的别名。
property:映射到列结果的属性。可以映射简单的如“username”这样的属性,也可以映射一些复杂对象中的属性,例如“address.street.number”,这会通过“.”方式的属性嵌套赋值。
javaType:一个Java类的完全限定名,或一个类型别名(通过typeAlias配置或者默认的类型)。如果映射到一个JavaBean,MyBatis通常可以自动判断属性的类型。如果映射到HashMap,则需要明确地指定javaType属性。
jdbcType:列对应的数据库类型。JDBC类型仅仅需要对插入、更新、删除操作可能为空的列进行处理。这是JDBCjdbcType的需要,而不是MyBatis的需要。
typeHandler:使用这个属性可以覆盖默认的类型处理器。这个属性值是类的完全限定名或类型别名
如何定义返回值
resultMap:用resultMap来设置映射
resultType:根据读取结果和对应类自动映射,可以在select中使用别名,来和类的属性名对应
5、多结果查询例子
接口方法
/** * 查询全部用户 * * @return */ List<SysUser> selectAll();
XML设置
<select id="selectAll" resultType="tk.mybatis.simple.model.SysUser"> select id, user_name userName, user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, create_time createTime from sys_user </select>
这个例子中,select标签中直接使用resultType,以及使用字段别名,使sql的字段名与类的字段名对上,即可自动映射
6、名称映射规则
综上,有2种方式:1、在resultMap中配置property属性和column属性的映射;2、SQL中设置别名
property或者别名要与对象的属性名对应才能匹配,实际匹配时,是把字母都转换成大写来匹配的,所以不区分大小写;一般为了阅读,应该统一写法就OK
一种很常见的情况,数据库使用下划线命名方式,如user_Name;而Java中使用驼峰式命名,如userName
MyBatis提供了一个配置,自动将这2种方式进行匹配,在配置文件中设置即可,代码如下
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
7、添加单元测试代码
在src/test/java下的tk.mybatis.simple.mapper包中,添加基础测试类
package tk.mybatis.simple.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.junit.BeforeClass; /** * 基础测试类 */ public class BaseMapperTest { private static SqlSessionFactory sqlSessionFactory; @BeforeClass public static void init(){ try { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); reader.close(); } catch (IOException ignore) { ignore.printStackTrace(); } } public SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
修改CountryMapperTest类
因为Country和User的Mapper中都有SelectAll,所以不在唯一,需要用全限定类名tk.mybatis.simple.mapper.CountryMapper.selectAll去调用
package tk.mybatis.simple.mapper; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import tk.mybatis.simple.model.Country; public class CountryMapperTest extends BaseMapperTest { @Test public void testSelectAll() { SqlSession sqlSession = getSqlSession(); try { List<Country> countryList = sqlSession.selectList("tk.mybatis.simple.mapper.CountryMapper.selectAll"); printCountryList(countryList); } finally { sqlSession.close(); } } private void printCountryList(List<Country> countryList) { for (Country country : countryList) { System.out.printf("%-4d%4s%4s\n", country.getId(), country.getCountryname(), country.getCountrycode()); } } }
修改mybatis-config.xml,把CountryMapper.xml加入到Mapper配置中
<mappers> <mapper resource="tk/mybatis/simple/mapper/CountryMapper.xml" /> <package name="tk.mybatis.simple.mapper" /> </mappers>
完成上面设置后即可进行单元测试
8、一些复杂用法
a、关联查找某个表的数据
接口方法
/** * 根据用户 id 获取角色信息 * * @param userId * @return */ List<SysRole> selectRolesByUserId(Long userId);
XML配置
<select id="selectRolesByUserId" resultType="tk.mybatis.simple.model.SysRole"> select r.id, r.role_name roleName, r.enabled, r.create_by createBy, r.create_time createTime, u.user_name as "user.userName", u.user_email as "user.userEmail" from sys_user u inner join sys_user_role ur on u.id = ur.user_id inner join sys_role r on ur.role_id = r.id where u.id = #{userId} </select>
因为使用了自定义类型Enable,所以在mybatis-config.xml加入自定义类型处理器
<typeHandlers> <typeHandler javaType="tk.mybatis.simple.type.Enabled" handler="tk.mybatis.simple.type.EnabledTypeHandler"/> </typeHandlers>
这种情况,虽然有关联表查询,但是只是一个实体的数据,所以只是sql中加入了join语句的不同,其它和单个表查询基本相同
b、关联查询多个表数据
例如a中,我不仅要查询角色,也要带上用户的信息,那就是要查询出2个表的信息了,那么可以如下这么做
第一种:把用户的信息增加到角色表中;不过这种方式不建议
package tk.mybatis.simple.model; public class SysRoleExtend extends SysRole { private String userName; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
第二种:把user表加到角色表中
public class SysRole implements Serializable { /** * 用户信息 */ private SysUser user; ......
修改mapper,使用“.”的方式增加user需要的信息
<select id="selectRolesByUserId" resultType="tk.mybatis.simple.model.SysRole"> select r.id, r.role_name roleName, r.enabled, r.create_by createBy, r.create_time createTime, u.user_name as "user.userName", u.user_email as "user.userEmail" from sys_user u inner join sys_user_role ur on u.id = ur.user_id inner join sys_role r on ur.role_id = r.id where u.id = #{userId} </select>
测试代码
@Test public void testSelectRolesByUserId(){ SqlSession sqlSession = getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //调用 selectRolesByUserId 方法查询用户的角色 List<SysRole> roleList = userMapper.selectRolesByUserId(1L); //结果不为空 Assert.assertNotNull(roleList); //角色数量大于 0 个 Assert.assertTrue(roleList.size() > 0); } finally { //不要忘记关闭 sqlSession sqlSession.close(); } }
浙公网安备 33010602011771号