MyBatis-02 Sql映射文件
一、Sql映射文件顶级配置元素
- mapper:映射文件的根元素节点,
仅有一个属性namespace(命名空间)
属性作用:
(1)区分不同的mapper,全局唯一
(2)绑定DAO接口,即面向接口 编程,当namespace绑定某一接口后,可以不用写该接口的实现类,MyBatis会通过接口的完全限定名查找到对应的mapper 配置来执行sql语句,因此namespace的命名必须要跟接口同名 - cache:配置给定命名空间的缓存
- cache-ref:从其他命名空间引用缓存配置
- resultMap:用来描述数据库结果集和对象的对应关系
- sql:可重用的sql块,也可以被其他语句引用
- insert/delete/update/select:映射增删改查语句
注: 关于MyBatis的sql映射文件中的mapper元素的namespace属性
- namespace的命名必须跟某个DAO接口同名,且同属于DAO层,故代码结构中,映射文件与该DAO
接口在同一package下,并且习惯上都是以Mapper结尾,如(UserMapper.xml、UserMapper.java) - 在不同的mapper文件中,子元素的id可以相同,MyBatis通过namespace和子元素的id联合区分。
接口中的方法(名)与映射文件中的sql语句id应一一对应。
二、select查询操作
注:以下代码中主要显示sql映射文件中语句,其他环境配置等代码见初始MyBatis-1
1、使用select 完成单条件查询操作
UserMapper.xml
<!-- 根据用户名称模糊查询用户列表 -->
<select id="getUserListByUserName" resultType="user" parameterType="string">
select * from smbms_user where userName like
CONCAT('%',#{userName},'%')
<!--以上sql语句相当于JDBC的语句如下 -->
<!-- PreParedStatement pstmt=conn.prePareStatement(sql); ps.setString(1,userName); -->
</select>
UserMapper.java
/**
* 根据用户名模糊查询用户列表
* @param userName 用户姓名
* @return User列表
* @throws Exception
*/
public List<User> getUserListByUserName(String userName)throws Exception;
2、使用select完成多条件传操作
- 封装为对象入参
UserMapper.xml
<select id="getUserList" resultType="user" parameterType="User">
<!--根据用户名称、用户角色模糊查询;因有多个条件,所以将条件封装为对象查询-->
select * from smbms_user where userName like
CONCAT('%',#{userName},'%')
and userRole=#{userRole}
</select>
UserMapper.java
/**
* 查询用户列表(对象入参)
* @param user
* @return
* @throws Exception
*/
public List<User> getUserList(User user)throws Exception;
- 封装为Map入参
UserMapper.xml
<!--查询用户列表 -->
<select id="getUserListByMap" resultType="user" parameterType="hashmap">
<!--将多个条件封装为Map集合,而对应的parameterType类型为hashmap-->
select * from smbms_user where userName like
CONCAT('%',#{userName},'%')
and userRole=#{userRole}
</select>
UserMapper.java
/**
* 查询用户列表(map入参)
* @param user
* @return
* @throws Exception
*/
public List<User> getUserListByMap(Map<String,String> userMap)throws Exception;
3、使用resultMap完成查询结果的展现
需求: 查询所有用户列表,其中的角色信息要以角色名称(userRoleName)展示,而User表中只有userId属性且与Role(角色表)主外键关系对应。
解决方案:
- 方案一 修改pojo:User.java类,增加userRoleName属性,并修改sql映射文件UserMapper.xml的sql语句,对用户表和Role角色表进行连表查询,做resultType自动映射。
- 方案二 通过resultMap来映射自定义结果
注:方案一不做演示,推荐使用方式为第二种
UserMapper.java不做更改
User.java
添加属性userRoleName
private String userRoleName; //角色名称
public String getUserRoleName() {
return userRoleName;
}
public void setUserRoleName(String userRoleName) {
this.userRoleName = userRoleName;
}
UserMapper.xml
<!--根据角色id和用户姓名模糊查询用户列表 -->
<!---resultMap属性值对应resultMap元素的id属性值->
<select id="getUserList" resultMap="userList" parameterType="user">
select u.*,r.roleName from smbms_user as u,smbms_role as r
where u.userRole=r.id and u.userRole=#{userRole}
and u.userName like CONCAT('%',#{userName},'%')
</select>
<resultMap type="User" id="userList">
<!-- property为属性名 column对应数据库列名 -->
<result property="id" column="id" />
<result property="userCode" column="userCode" />
<result property="userName" column="userName" />
<result property="phone" column="phone" />
<result property="birthday" column="birthday" />
<result property="gender" column="gender" />
<result property="userRole" column="userRole" />
<result property="userRoleName" column="roleName" />
</resultMap>
UserMapperTest.java测试类
/**
* 查询用户列表
*/
@Test
public void testGetUserList() {
SqlSession sqlSession = null;
List<User> ulist = null;
try {
sqlSession = MyBatisUtil.createSqlSession();
// 对象入参
User user = new User();
user.setUserName("赵");
user.setUserRole(3);
ulist = sqlSession.getMapper(UserMapper.class).getUserList(user);
for (User u : ulist) {
logger.info("用户编码:"+u.getUserCode()+",用户姓名:"+u.getUserName()+",用户角色:"+u.getUserRoleName()+",用户地址:"+u.getAddress());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
}
讲解:
- resultMap元素的属性值和子节点
id属性 唯一标识,此id值用于select元素resultMap属性的引用
type属性 表示该resultMap的映射结果类型
result子节点 用于标识一些简单属性,
result节点的属性:1、column属性表示从数据库中查询的字段名
2、property表示查询出来的字段值赋给实体对象的哪个属性 MyBatis中在对查询进行select映射时的两种方式resultType和resultMap之间的关联与区别
区别:
- resultType直接表示返回类型,包括基础数据类型和复杂数据类型。
- resultMap则是对外部resultMap定义的引用,对应外部resultMap的id,表示返回结果映射到哪一个resultMap上。应用场景:数据库字段信息和对象属性不一致或需要做复杂的联合查询。
关联:
在使用MyBatis进行查询映射时,实际上查询出的每个字段信息都放在一个对应的map里面,key对象字段名,value对应相应的值,当select元素提供的返回类型属性为resultType时,MyBatis会将map中的键值对取出赋给resultType所指定的对象的属性(即调用对象属性的setter方法)。
因此,当使用resultType,直接在后台就能接收到相应的对象属性值。由此可看出,其实MyBatis的每个查询映射的返回值类型都是resultMap,只是当提供的返回值类型是resultType时,MyBatis会自动把对应的值赋给指定对象的属性,而当返回值类型为resultMap时,因为map不能很好的表示领域模型,所以需要手动转化为对应的实体对象。( 领域模型:业务对象模型[对象])
注:
- 在MayBatis的select元素中,resultType与resultMap本质是一样的,都是Map数据结构,
但resultType和resultMap属性绝对不能同时存在,只可二选一 - resultMap的自动映射级别:使用resultMap自动映射的前提是属性名和自动名必须一致,在默认映射级别(PARTIAL)情况下,如果没有做匹配也可以在后台获取到未匹配过的属性值;若不一致且在resultMap中没有做映射,则无法获取到数据
三、增删改操作
1、insert插入操作
UserMapper.java
/**
* 添加用户
* @param user
* @return
*/
public int add(User user)throws Exception;
UserMapper.xml
<!-- 新增 -->
<insert id="add" parameterType="User">
insert into
smbms_user(userCode,userName,userPassword,gender,birthday,phone,
address,userRole,createdBy,creationDate)
values(#{userCode},#{userName},#{userPassword},#{gender},#{birthday},
#{phone},#{address},#{userRole},#{createdBy},#{creationDate})
</insert>
2、update更新操作
UserMapper.java
/**
* 修改用户
* @param user
* @return
*/
public int modify(User user)throws Exception;
UserMapper.xml
<!-- 修改 -->
<update id="modify" parameterType="User">
UPDATE smbms_user set userCode=#{userCode},
userName=#{userName},userPassword=#{userPassword},gender=#{gender},
birthday=#{birthday},phone=#{phone},address=#{address},
userRole=#{userRole},modifyBy=#{modifyBy},modifyDate=#{modifyDate}
WHERE id=#{id}
</update>
3、使用@Param注解实现多参数注入
需求: 修改指定用户的个人密码
分析: 可以明确方法传入的参数有两个,用户id和新密码,需要修改列只有一个,所以将条件参数封装为对象不合适,所以此处运用@Param实现多参数传递,代码如下
UserMapper.java
/**
* 为指定用户修改密码
* @param id
* @param pwd
* @return
*/
public int updatePwd(@Param("id")Integer id,@Param("userPassword")String pwd)throws Exception;
UserMapper.xml
<!-- 修改密码 -->
<update id="updatePwd">
update smbms_user set userPassword=#{userPassword}
where id=#{id}
</update>
UserMapperTest.java测试类
/**
* 修改密码
*/
@Test
public void testUpdatePwd() {
SqlSession sqlSession = null;
int count = 0;
try {
sqlSession = MyBatisUtil.createSqlSession();
count = sqlSession.getMapper(UserMapper.class).updatePwd(16, "888888");
// int i=2/0; //模拟操作失败
sqlSession.commit(); //提交
} catch (Exception e) {
sqlSession.rollback(); //回滚
e.printStackTrace();
count = 0;
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
logger.debug("updatePwd count---->" + count);
}
讲解:
- 使用@Param与不使用的区别
若不使用@Param注解,则会报错,报错信息类似于Parameter’参数名’
not found。深入MyBatis源码,MyBatis的参数类型为Map,若使用 @Param注解参数,那么就会记录指定的参数名(@Param后括号内的名称)为key;若参数没有加@Param,那么就会记录”param”+它的序号作为Map的key,故在进行多参数入参时,若没有使用@Param指定参数,那么映射的sql语句中获取不到#{参数名}
,从而报错。 - @Param注解参数使用场景
(1)一般情况下,超过4个以上的参数最好封装为对象(特别是常规的增加、修改操作,字段较多的情况)
(2)对于参数固定,推荐使用@Param,原因是此方法较灵活,代码可读性高
注: 当参数为基础数据类型时,不管是多参数入参还是单独一个参 数,都需要使用@Param注解参数
4、delete删除操作
UserMapper.java
/**
* 删除指定用户
* @param id
* @return
*/
public int deleteUserById(@Param("id")Integer id)throws Exception;
UserMapper.xml
<!-- 删除指定用户 -->
<delete id="deleteUserById" parameterType="Integer">
delete from smbms_user
where id=#{id}
</delete>
四、实现MyBatis高级结果映射
1、association(一对一):
映射到某个JavaBean的某个复杂类型数据,比如Java类,即JavaBean内部嵌套一个复杂数据类型的属性,这种情况就属于复杂类型的关联。注:association仅处理一对一的关联关系。
属性
- javaType:完整java类名或别名,若映射到一个JavaBean,则MyBatis通常会自行监测到其类型;若映射到一个HashMap,则应该明确指定javaType,来确保其行为
- property:映射数据库列的实体对象的属性名子元素
property子元素
1、id:对应数据库中改行的主键id
2、result映射数据库列
result属性
1)property:映射数据列的实体对象的属性
2)column:数据库列名或别名
注: 在做映射的过程中,要确保所有的列名都是唯一且无歧义的
Demo演示
需求: 根据角色id获取用户列表
User.java添加属性Role对象
private Role role; //角色
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
Role.java
package cn.smbms.pojo;
import java.util.Date;
/**
* 用户角色类
* @author 14062
*
*/
public class Role {
private Integer id; //主键id
private String roleCode; //角色编码
private String roleName; //角色名称
private Integer createdBy; //创建者
private Date creationDate; //创建日期
private Integer modifyBy; //修改者
private Date modifyDate; //修改日期
//省略getter/setter方法
}
UserMapper.java
/**
* 根据roleId查询用户列表
* @param roleId
* @return
*/
public List<User> getUserListByRoleId(@Param("userRole")Integer roleId)throws Exception;
UserMapper.xml
<!-- 根据角色id获取用户列表 -->
<select id="getUserListByRoleId" resultMap="userRoleResult"
parameterType="Integer">
select u.*,r.id as r_id,r.roleCode,r.roleName from
smbms_user as u,smbms_role as r
where userRole=#{userRole} and u.userRole=r.id
</select>
<resultMap type="user" id="userRoleResult">
<!--唯一标识结果集 -->
<id property="id" column="id" />
<result property="gender" column="gender" />
<result property="userCode" column="userCode" />
<result property="userName" column="userName" />
<result property="userRole" column="userRole" />
<result property="address" column="address" />
<!-- 映射复杂数据类型的属性 -->
<!-- 实现association结果映射的复用 resultMap属性值指向对应resultMap元素的id属性值 -->
<association property="role" javaType="Role" resultMap="roleResult" />
</resultMap>
<!-- association复用 -->
<resultMap type="role" id="roleResult">
<!--查出数据后为id起了别名r_id 唯一标识结果集 -->
<id property="id" column="r_id" />
<result property="roleCode" column="roleCode" />
<result property="roleName" column="roleName" />
</resultMap>
UserMapperTest.java测试类
/**
* 根据角色id获取用户列表
*/
@Test
public void testGetUserListByRoleId() {
SqlSession sqlSession = null;
List<User> list = new ArrayList<User>();
try {
sqlSession = MyBatisUtil.createSqlSession();
list = sqlSession.getMapper(UserMapper.class).getUserListByRoleId(2);
logger.info("符合条件的用户数:" + list.size());
for (User user : list) {
logger.info("用户姓名:" + user.getUserName());
logger.info("用户角色:" + user.getRole().getId());
logger.info("用户角色:" + user.getRole().getRoleName());
logger.info("角色编码:" + user.getRole().getRoleCode());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
}
2、collection(一对多):
作用与association类似,也是映射到JavaBean的某个复杂数据类型的 属性,区别是这个类型是一个集合列表,即JavaBean内部嵌套一个复杂数据类型(集合)属性。
属性
- ofType:完整java类名或别名,即集合所包含的类型
- property:映射实体对象中的(集合类型)属性名
例:<collection property="addressList" ofType="Address"></collection>
注: 以上代码可以理解为一个属性名为adressList,类型为Address的 集合
Demo演示
需求: 获取指定用户的相关信息和地址列表
分析: 一个用户有多个地址,用户对地址为一对多的关系
User.java添加属性List<Address>
地址集合
private List<Address> addressList; //用户地址列表
public List<Address> getAddressList() {
return addressList;
}
public void setAddressList(List<Address> addressList) {
this.addressList = addressList;
}
UserMapper.java
/**
* 获取指定用户的相关信息和地址列表
*/
public List<User> getAddressListByUserId(@Param("id")Integer userId);
UserMapper.xml
<!-- 根据用户id查询用户地址信息 -->
<select id="getAddressListByUserId" resultMap="userAddressResult"
parameterType="Integer">
select u.*,a.id as
a_id,a.contact,a.addressDesc,a.postCode,a.tel,userId
from smbms_user as
u,smbms_address as a
where u.id=#{id} and u.id=a.userId
</select>
<resultMap type="user" id="userAddressResult">
<id property="id" column="id" />
<result property="userCode" column="userCode" />
<result property="userName" column="userName" />
<!--使用collection一对多 高级映射 ofType为JavaBean类型 -->
<collection property="addressList" ofType="Address"
resultMap="addressResult" />
</resultMap>
<!-- 根据用户id获取地址新列表 collection复用 -->
<resultMap type="address" id="addressResult">
<id property="id" column="a_id" />
<result property="postCode" column="postCode" />
<result property="tel" column="tel" />
<result property="contact" column="contact" />
<result property="addressDesc" column="addressDesc" />
</resultMap>
UserMapperTest.java
/**
* 根据用户id获取地址列表-测试
*/
@Test
public void testGetAddressListByUserId() {
SqlSession sqlSession = null;
List<User> list = new ArrayList<User>();
try {
sqlSession = MyBatisUtil.createSqlSession();
list = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
logger.info("用户数量:" + list.size());
if (list.size() > 0) {
for (User u : list) {
logger.info("用户姓名:" + u.getUserName());
logger.info("用户密码:"+u.getUserPassword());
logger.info("--------------地址列表-----------------");
for (Address a : u.getAddressList()) {
logger.info("用户id:"+a.getUserId());
logger.info("联系人:" + a.getContact());
logger.info("地址详情:" + a.getAddressDesc());
logger.info("邮政编码:" + a.getPostCode());
}
}
}
}
五、MyBatis的自动映射级别
- NONE:禁止自动匹配
- PARTIAL(默认):自动匹配所有属性,有内部嵌套的除外
- FULL:匹配所有
六、MyBatis执行原理
mybatis运行时要先通过resources把核心配置文件也就是mybatis.xml文件加载进来,然后通过xmlConfigBulider来解析,解析完成后把结果放入configuration中,并把它作为参数传入到build()方法中,并返回一个defaultSQLSessionFactory。我们再调用openSession()方法,来获取SqlSession,在构建SqlSession的同时还需要transaction和executor用于后续执行操作。 推荐文章MyBatis运行原理
还没有评论,来说两句吧...