MyBatis 使用笔记
resultMap 自定义结果集映射
查询结果集与映射pojo属性不一致时,可以使用resultMap指定结果集列名与pojo的属性名进行手动映射
手动指定 主键列名 与 普通列名 跟 javabean 属性进行一一映射
(就算结果集列名跟属性名一致,推荐也是进行映射)
<!--自定义结果集
1. resultMap
id:唯一标识,用于被 select 标签中的 resultMap 属性所引用
type:指当前结果集中的列名跟哪个 JavaBean 的属性去对应,最终映射到哪个 JavaBean 中
2. 子标签:
id: 代表映射主键列名
property: JavaBean中的属性名
column:结果集列名
result:映射普通列
property: JavaBean中的属性名
column:结果集列名
-->
<!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
<resultMap id="userMap" type="com.itheima.domain.User">
<!-- 主键字段的对应 -->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="username"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
一对一(多对一):association
==javaType:指定具体关联的属性类型 #f44336==
<!--
自定义结果集封装
映射 一对一 或者 多对一
-->
<resultMap id="accountAndUserResultMap" type="com.itheima.domain.Account">
<!--
帐户信息 封装成功
-->
<id property="id" column="id"></id>
<result property="money" column="money"></result>
<result property="uid" column="uid"></result>
<!--
association : 映射 一对一 的关联关系
property : 关联信息的 属性
column : 关联属性对应的列名
javaType : 关联属性的具体类型
-->
<association property="user" column="uid" javaType="com.itheima.domain.User">
<id property="id" column="uid"></id>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
</association>
</resultMap>
<!--
List<Account> findAccountAndUser();
-->
<select id="findAccountAndUser" resultMap="accountAndUserResultMap">
SELECT
a.id,
a.money,
u.id uid,
u.username,
u.birthday,
u.sex,
u.address
FROM
account a
LEFT JOIN USER u
ON a.uid = u.id
</select>
一对多:collection
==ofType:指定具体关联的集合中元素的类型 #f44336==
<!--
resultMap 标签可以被继承 extends="baseResultMap"
baseResultMap 是指 mapper.xml 中某一个 resultMap 的 id
-->
<resultMap id="userAndAccountsResultMap" type="com.itheima.domain.User">
<id property="id" column="id"></id>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<!--
collection : 映射一对多关联关系
property : 关联信息的 属性
column : 关联属性对应的列名
javaType : 关联属性的类型
ofType : 集合中元素的具体类型
-->
<collection property="accounts" column="id" ofType="com.itheima.domain.Account">
<id property="id" column="aid"></id>
<result property="money" column="money"></result>
<result property="uid" column="uid"></result>
</collection>
</resultMap>
<!--
List<User> findUserAndAccounts();
-->
<select id="findUserAndAccounts" resultMap="userAndAccountsResultMap">
SELECT
u.id,
u.username,
u.birthday,
u.sex,
u.address,
a.id aid,
a.money,
a.uid
FROM
USER u
LEFT JOIN
account a
ON a.uid = u.id
</select>
多对多映射:collection,在 mybatis 中其实也是看成 一对多
需要借助 中间表
userid roleid 联合主键
1 1
2 1
1 2
<!--定义role表的ResultMap-->
<resultMap id="roleMap" type="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
### 输入映射 (参数封装)parameterType
简单参数(8大基本数据类型或其包装类型,还有 String 类型)
#{key} key随意,一般key与形参名称一致
pojo #{key} key名必须与 pojo 的属性名一致,具体参看 增删改配置
包装 pojo(QueryVo) 参数名必须与 包装 pojo 的属性一致(可以使用 . 的形式访问其属性)
#{user.username} 该 user 是 queryVo 的一个属性, username 是 user 的一个属性
上述ONGL表达式获取到的是 QueryVo中的user中的 username 的值
例如:
```java
class QueryVo{
private User user;
//省略 getter/setter 方法
}
一对一
查询 账户时,关联查询用户信息(用户信息实现延迟加载)
<resultMap id="baseResultMap" type="com.itheima.domain.Account">
<id property="id" column="id"></id>
<result property="money" column="money"></result>
<result property="uid" column="uid"></result>
<!--
select : 指定要执行的查询 SQL 语句所在的 statementId
column : 指的是 select 属性的值所需要的参数
fetchType : 抓取策略
lazy : 延迟加载 eager : 立即加载
-->
<association property="user" javaType="com.itheima.domain.User" column="uid"
select="com.itheima.dao.UserPlusMapper.findById" fetchType="lazy"></association>
</resultMap>
<!--
List<Account> findAll();
-->
<select id="findAll" resultMap="baseResultMap">
SELECT * FROM account
</select>
<!--
List<Account> findByUid(Integer uid);
-->
<select id="findByUid" resultMap="baseResultMap">
SELECT * FROM account where uid = #{uid}
</select>
需要在全局配置文件中配置延迟加载
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
一对多
<resultMap id="baseResultMap" type="com.itheima.domain.User">
<id property="id" column="id"></id>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<collection property="accounts" column="id" javaType="list" ofType="com.itheima.domain.Account"
select="com.itheima.dao.AccountPlusMapper.findByUid" fetchType="eager"></collection>
</resultMap>
<!--
User findById(Integer id);
-->
<select id="findById" resultMap="baseResultMap">
SELECT * FROM USER WHERE id = #{id}
</select>
<update id="update">
UPDATE USER SET username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE id = #{id}
</update>
@param注解
多个参数前,使用@param("name") 修饰形参,在 xml 中使用 #{name} 映射绑定形参
例如:
public UserInfo login(@Param("username") String username, @Param("password")String password);
@Param("key") 该注解为入参参数指定 key #{key}
<select id="login" resultType="com.itheima.domain.UserInfo">
SELECT * FROM user_info where username = #{username} and password = #{password}
</select>
映射结果封装
resultType:底层是 resultMap
简单类型:int、double、String 等,直接返回 类型全类名 或者 别名
譬如:int、java.lang.String 等
pojo:可以是全类名,也可以是别名。推荐使用全类名:包名.类名
譬如:com.itheima.domain.User
List 集合:是集合中元素的具体类型,格式同 pojo
map集合:java.util.map 或者 map
常用标签、属性
- 标签:mapper、select、insert、update、delete、resultMap、sql、include、cache、selectKey
select 标签
1、查询所有
<?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="dao接口全限定类名">
<!--List<User> findAll();-->
<!--配置查询所有-->
<select id="dao接口的方法名" resultType="JavaBean的全限定类名">
select * from user <!--原生SQL语句-->
</select>
</mapper>
2、查询单个
<select id="dao接口的方法名" resultType="JavaBean的全限定类名">
select * from user where id = #{id}
</select>
3、模糊查询
<select id="dao接口的方法名" resultType="JavaBean的全限定类名">
select * from user where username like #{username}
</select>
<!-- 当调用接口方法时,需要为传入参数前后加上 %,如 username="%王%" -->
4、查询返回单行单列,返回一个值
<select id="dao接口的方法名" resultType="long">
select count(1) from user
</select>
insert 标签
- 子标签:selectKey
<!--
insert: 代表新增
parameterType:可以省略不写,推荐写上
-->
<insert id="接口方法名" parameterType="接口方法中形参的全类名">
<!--
keyProperty : 返回的值 赋值给 JavaBean 的哪个属性
keyColumn : 返回的哪个列的值
resultType : select LAST_INSERT_ID() 返回值类型
order 表示 : select LAST_INSERT_ID() 的执行时机
AFTER 代表在插入语句执行成功之后执行 适用场景:自增主键
BEFORE 代表在插入语句执行之前执行 适用场景:非自增主键,主键是UUID()获取的字符串
-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select LAST_INSERT_ID()
</selectKey>
<!-- user 是表名 -->
insert into user (username,address) values (#{username},#{address})
</insert>
<!--
返回自增主键值的另一种写法
useGeneratedKeys : 是否支持主键自增 true : 支持 false : 不支持
keyProperty : 返回的值 赋值给 JavaBean 的哪个属性
keyColumn : 返回的哪个列的值
* 该方式支持 批量插入返回主键
-->
<insert id="saveReturnId" parameterType="com.itheima.domain.User" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
INSERT INTO USER (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address})
</insert>
<!--
返回非自增主键的值
以 UUID() 为例
-->
<insert id="接口方法名" parameterType="com.itheima.domain.User">
<!-- BEFORE 代表在插入语句执行之前 执行 用于主键是 UUID() 的情况 -->
<selectKey keyProperty="id" keyColumn="id" resultType="string" order="BEFORE">
select UUID()
</selectKey>
<!-- user_info 是表名 -->
insert into user_info (id,username) values (#{id},#{username})
</insert>
修改update和删除delete
<!-- 更新用户 -->
<update id="updateUser" parameterType="com.itheima.domain.User">
update user set username=#{username},address=#{address},sex=#{sex},
birthday=#{birthday} where id=#{id}
</update>
<!-- 删除用户-->
<delete id="deleteUser" parameterType="java.lang.Integer">
<!--
当入参参数为 8大基本数据类型或其包装类型,还有 String 类型时,入参参数在 sql映射文件中,
使用ONGL表达式取值,可以指定随意名称,只表示占位符,但是推荐使用方法形参名,提高可读性
-->
delete from user where id = #{id}
</delete>
动态sql
if、where、foreach、sql片段
if 、 where
<!--
List<User> findByCondition(User user);
-->
<select id="findByCondition" parameterType="com.itheima.domain.User" resultType="com.itheima.domain.User">
select * from user
<where>
<if test="username != null and username != ''">
and username = #{username}
</if>
<!--
3.4.5 不支持以下写法
<if test="sex == '男' or sex == '女'">
and sex = #{sex}
</if>
-->
<!-- 单引号中包含 双引号的形式 -->
<!--
<if test='sex == "男" or sex == "女"'>
and sex = #{sex}
</if>
-->
<!-- 把 传入的值 使用 toString() 转成 字符串 -->
<if test="sex == '男'.toString() or sex == '女'.toString()">
and sex = #{sex}
</if>
</where>
</select>
<!--
where 会自动过滤其中条件成立的多余最前面的 and 或者 or
if
test 表示条件判断
写的是 ognl 表达式,其中 判断的参数为 Javabean 的属性名
注意 :不能写特殊符号 譬如 && > <
等值判断写法,需要是 单引号中包含 双引号的形式
-->
foreach
<!--
foreach 循环
collection : 循环的集合
item : 当前正在遍历的对象
open : 开始的部分
close : 结束的部分
separator : 当前正在遍历的每个对象之间的分隔符
-->
<!--
List<User> findByIds(@Param("ids") List<Integer> ids);
-->
<select id="findByIds" parameterType="list" resultType="com.itheima.domain.User">
select * from user
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" item="userId" open="and id in (" close=")" separator=",">
#{userId}
</foreach>
</if>
</where>
</select>
<!--
int saveManyUser(@Param("users") List<User> users);
判断 集合是否为空,可以在 service 层进行业务处理
-->
<insert id="saveManyUser" parameterType="list">
INSERT INTO USER (username,birthday,sex,address) VALUES
<foreach collection="users" item="user" open="" close="" separator=",">
(#{user.username},#{user.birthday},#{user.sex},#{user.address})
</foreach>
</insert>
trim
<!--
trim
prefix : 条件拼接成立后要加的 前缀
prefixOverrides : 条件拼接成立后要去掉的前面多余的 字符串
suffix : 条件拼接成立后要加的 后缀
suffixOverrides : 条件拼接成立后要去掉的后面多余的 字符串
-->
<select id="findByCondition" parameterType="com.itheima.domain.User" resultType="com.itheima.domain.User">
select * from user
<trim prefix="where" prefixOverrides="" suffix="" suffixOverrides="and">
<if test="username != null and username != ''">
username = #{username} and
</if>
<if test="sex != null and sex != ''">
sex = #{sex} and
</if>
</trim>
</select>
set
<!--
set : 只能用于 update 标签中
会自动过滤条件成立 多余的部分(,)
-->
<update id="updateUser" parameterType="com.itheima.domain.User">
UPDATE USER
<set>
<if test="username != null">
username = #{username},
</if>
<if test="birthday != null">
birthday = #{birthday},
</if>
<if test="sex != null">
sex = #{sex},
</if>
<if test="address != null">
address = #{address},
</if>
</set>
<where>
<if test="id != null">
id = #{id}
</if>
</where>
</update>
sql 片段
<!-- 定义 sql片段,该片段可以是 mapper.xml 中的任何内容 -->
<sql id="base_column">
username,birthday,sex,address
</sql>
<!--
int saveManyUser(@Param("users") List<User> users);
判断 集合中是否为空,可以在 service 层进行业务处理
-->
<insert id="saveManyUser" parameterType="list">
INSERT INTO USER (
<!-- include 标签 的 refid 属性用于引用 当前 mapper.xml 中已经定义的 sql 片段 -->
<include refid="base_column"></include>
) VALUES
<foreach collection="users" item="user" open="" close="" separator=",">
(#{user.username},#{user.birthday},#{user.sex},#{user.address})
</foreach>
</insert>
sql 的子节点
- if、where、foreach、trim、set、choose、when、otherwise
- 属性:namespace、id、resultType、resultMap、parameterType
<select id="findAll" resultMap="roleMap">
select u.*,r.id as rid,r.role_name,r.role_desc from role r
left outer join user_role ur on r.id = ur.rid
left outer join user u on u.id = ur.uid
</select>
据 id 查询用户
* @param id
* @return
*/
User findById(Integer id);
/**
* 更新
* 没有返回值
* @param user
*/
void update(User user);
}
注解配置
- @Insert:实现新增
- @Update:实现更新
- @Delete:实现删除
- @Select:实现查询
- @Result:实现结果集封装
- @Results:可以与@Result 一起使用,封装多个结果集 (等同于 xml 中的 ResultMap)
- @ResultMap:实现引用@Results 定义的封装
- @One:实现一对一结果集封装
- @Many:实现一对多结果集封装
- @SelectProvider: 实现动态 SQL 映射
- @CacheNamespace:实现注解二级缓存的使用
一对一
/**
* 查询所有账户,并且获取每个账户所属的用户信息
* @return
*/
@Select("select * from account")
@Results(id="accountMap",value = {
@Result(id=true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(property = "user",column = "uid",
one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
})
List<Account> findAll();
一对多
/**
* @Results 等同于 Sql映射文件(mapper.xml) 中 resultMap 节点
* id : 唯一标识,以便其他@Select注解标记的方法引用结果集
* 等同于 Sql映射文件(mapper.xml) 中 resultMap 节点中的 id属性
* value : 指定如何映射结果集
* @Result
* id : 代表映射是否是主键 true:是 false:不是 (默认值)
*
* @ResultMap 表示引用 其他 @Results的 id属性的值
* @return
*/
@Select("select * from user")
@Results(id="userMap",value={
@Result(id=true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "address",property = "userAddress"),
@Result(column = "sex",property = "userSex"),
@Result(column = "birthday",property = "userBirthday"),
@Result(property = "accounts",column = "id",
many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
fetchType = FetchType.LAZY))
})
List<User> findAll();
8、延迟加载(懒加载)
在关联查询的基础上才可能出现延迟加载(发送两条或两条以上的SQL)
多对一(一对一)或者一对多 都可以使用懒加载
AccountPlusMapper.java
/**
* @author 黑马程序员
* @Company http://www.ithiema.com
*/
public interface AccountPlusMapper {
/**
* 查询帐户关联用户信息
* @return
*/
List<Account> findAll();
/**
* 根据用户id查询账户列表
* @param id
* @return
*/
List<Account> findByUid(Integer uid);
}
UserPlusMapper.java
/**
* 也是操作 User类的持久化接口
* @author ghy
*/
public interface UserPlusMapper {
/**
* 根
## 缓存
```java
/**
* 测试一级缓存
* 注意:只有两次相同查询操作才会有缓存.
* 1.二级缓存是 SqlSession 级别,也就是只有同一个 SqlSession 才具备相同的缓存。一级缓存存放的数据是 对象的副本
* 2.一级缓存默认开启,程序员无法关闭。一级缓存的介质通常是内存
*
* 3.当 SqlSession 对象执行以上方法时,缓存失效
* close
* clearCache
* commit
* 增删改方法,不管是否提交事务
*/
@Test
public void testFirstLevelCache(){
User u1 = userPlusMapper.findById(88);
System.out.println(u1);
//session.commit();
User user = new User();
user.setId(87);
user.setUsername("87改一下");
userPlusMapper.update(user);
User u2 = userPlusMapper.findById(88);
System.out.println(u2);
System.out.println(u1 == u2);
}
/**
* 测试二级缓存
* 注意:在 SqlSession 关闭之后,数据才会保存到二级缓存中
* 1.二级缓存是 SqlSessionFactory 级别,由同一个 SqlSessionFactory 生产的 SqlSession 共享一个二级缓存。
* 二级缓存存放的数据是 散装数据
* 2.二级缓存默认开启(3.4.5版本,之前的版本默认是有关闭的),程序员可以手动开启。二级缓存的介质可以是内存或者硬盘
* 3.二级缓存使用步骤
* 3.1 要缓存的类必须实现 Serializable 接口
* 3.2 全局配置文件中开启
* 3.3 在要使用二级缓存的 Mapper.xml文件中 使用 <cache/>
*
* 当用户发送查询需求时,查询操作是怎么执行的?
*/
@Test
public void testSecondLevelCache(){
SqlSession session1 = sqlSessionFactory.openSession();
UserPlusMapper userPlusMapper1 = session1.getMapper(UserPlusMapper.class);
User user1 = userPlusMapper1.findById(79);
session1.close();
SqlSession session2 = sqlSessionFactory.openSession();
UserPlusMapper userPlusMapper2 = session2.getMapper(UserPlusMapper.class);
User user2 = userPlusMapper2.findById(79);
session2.close();
System.out.println(user1 == user2); // false
}
逆向工程(Mybatis官方提供的)
- 就是由 数据库表 生成 pojo,mapper接口,mapper.xml (只能生成单表CRUD)
1、改数据库的连接信息
2、改包名
3、指定要逆向生成的表名与类名
tb_user (表名) ---- TbUser (默认的类名) ---- User (自定义类名)
双击运行插件 ---- idea