MyBatis2(MyBatis 开发DAO的方法)
SqlSession使用范围
SqlSessionFactoryBuilder(当作工具类来使用)
- 通过 SqlSessionFactoryBuilder().builder(InputStream is) 创建 SqlSessionFactory,
在创建SqlSessionFactory的时候,只需要new 一次SqlSessionFactoryBuilder即可
SqlSessionFactory
- 通过 SqlSessionFactory 创建 SqlSession, 通过单例模式管理sqlSessionFactory (一旦创建,不再销毁,一直使用)
将来 Spring 和 Mybatis 整合后,使用单例模式管理 sqlSessionFactory
SqlSession
- SqlSession 面向用户(开发者)的接口,但是是线程不安全的,最佳的应用场合是在方法体内,定义成局部变量
##DAO 开发
原始Dao 开发方法(需要写Dao 接口和Dao 实现类)
- 向Dao 实现类中注入SqlSessionFactory, 通过sqlSessionFactory 创建SqlSession
dao 接口
public interface Dao {
// 通过用户id 查询用户
User findUserById(int id) throws Exception;
// 通过用户名字模糊查询用户
List<User> findUserByName(String name) throws Exception;
// 添加用户
int addUser(User user) throws Exception;
// 通过id 删除用户
int deleteUserById(User user) throws Exception;
// 通过id 更新用户
int updateUserById(User user) throws Exception;
}
dao 实现类
public class DaoImpl implements Dao {
private SqlSessionFactory sqlSessionFactory;
public DaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
User u = sqlSession.selectOne("com.mybatisStudy2.usermapper.findUserById", id);
sqlSession.close();
return u;
}
@Override
public List<User> findUserByName(String name) throws Exception {
name = ("%").concat(name).concat("%");
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> users = sqlSession.selectList("com.mybatisStudy2.usermapper.findUserByName", name);
sqlSession.close();
return users;
}
@Override
public int addUser(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
int num = sqlSession.insert("com.mybatisStudy2.usermapper.addUser", user);
sqlSession.commit();
sqlSession.close();
return num;
}
@Override
public int deleteUserById(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
int num = sqlSession.insert("com.mybatisStudy2.usermapper.deleteUserById", user);
sqlSession.commit();
sqlSession.close();
return num;
}
@Override
public int updateUserById(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
int num = sqlSession.insert("com.mybatisStudy2.usermapper.updateById", user);
sqlSession.commit();
sqlSession.close();
return num;
}
}
原始DAO 开发的问题
- 接口实现类存在大量的重复代码
- 在调用SqlSession 讲statement的ID 硬编码在Java 语句内
- sqlSession 方法传参错误在编译时不会报错,不利于开发
使用mapper 代理的方式(只需要mapper 接口(相当于Dao 接口))
- 编写Mapper 接口(相当于DAO 接口)
- 编写Mapper.xml 映射文件
- 规范
- 在Mapper.xml 的namespace 要为mapper 接口的地址
- 编写接口的方法,方法名和Mapper.xml 的statement 的id 一致
- Mapper 接口的输入参数要和Mapper.xml 内Statement 的 parameterType 指定的类型一致
- Mapper 接口的返回值要和Mapper.xml 内Statement 的 resultType 指定的类型一致
public interface Dao {
// 通过用户id 查询用户
User findUserById(int id) throws Exception;
// 通过用户名字模糊查询用户
List<User> findUserByName(String name) throws Exception;
// 添加用户
int addUser(User user) throws Exception;
// 通过id 删除用户
int deleteUserById(User user) throws Exception;
// 通过id 更新用户
int updateById(User user) throws Exception;
}
- 加载Mapper
<mapper resource="mapper/mapper.xml"/>
代理内部调用方式
@Test
public void test() throws Exception {
//读取配置文件
InputStream is = Resources.getResourceAsStream("mybatisConfig.xml");
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//实例化Dao
Dao dao = sqlSession.getMapper(Dao.class);
System.out.println(dao.findUserById(2).toString());
}
注:DAO 层参数建议使用pojo 类来包含所有参数(许多Service 层使用)、Service 层不建议使用pojo 传参\
Mybatis 配置文件(Mybatis 全局配置文件)
properties(属性)
- 讲数据库的配置单独配置到db.properties 中,在Mybatis内加载properties文件,避免硬编码
方便对参数进行统一管理
- db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/javaend
jdbc.username=root
jdbc.password=1006
- 加载properties
<properties resource="db.properties"></properties>
注:Mybatis 加载属性的顺序
- 首先读取properties 元素的值
- 再读取properties 元素内resource 或url 加载的属性,它会覆盖读取的同名属性
- 最后读取parameterType 传递的属性值
当properties 的属性名和mapper 内的属性名一样的时候(${jdbc.username})会出现如下错误
在properties 定义属性要有一定的特殊性,如:xxxx.xxxx
settings 全局参数配置
- mybatis 全局配置参数,全局参数将会影响mybatis 的运动行为
settings 配置
typeAliases (别名)
- 在输入类型的时候, 针对输入或输入的类型过长(全路径)不便于开发,可以使用别名
- 默认的别名
- 自定义别名 (pojo)
单个别名的定义
<typeAliases>
<!-- 针对单个别名
type:类型的路径
alias:别名 -->
<typeAlias type="com.pojo.User" alias="user"/>
</typeAliases>
批量定义别名(常用)
<typeAliases>
<!-- 批量定义别名:
指定包名,mybatis 自动将该包下面的pojo 设置别名(类名首字母小写(或大写))-->
<package name="com.pojo"/>
</typeAliases>
typeHandles 类型处理器
- 在Mybatis 使用typeHandles 完成jdbc 类型和java 类型的相互转换
mapper 映射器
- 记载单个的映射文件
<mappers>
<mapper resource="mapper/usermapper.xml"/>
<mapper resource="mapper/mapper.xml"/>
</mappers>
- 使用mapper 接口加载
- 需要将mapper 接口的类名和mapper.xml 玩文件名需要一致(前提:使用mapper 代理的方式)
<mappers>
<mapper class="com.dao.Dao"/>
</mappers>
-
使用批量加载
<mappers>
<package name="com.dao"/>
</mappers>
Mybatis 输入映射和输出映射
输入输出映射类型
- 简单类
- hashMap
- Pojo 包装对象
输入映射
- 使用包装类型
输出映射
- resultType
查询出来的列名称和pojo属性一致,pojo内属性才能被赋值
- resultMap
使用resultMap 完成一对一、多对多查询等
- 定义resultMap
- 使用resultMap 作为输出类型
<!-- 通过用户id 查询用户-->
<!-- resultMap:指定resultMap 的id,如果resultMap 在其他mapper.xml 下,则前面需要加上namespace -->
<select id="findUserById" parameterType="int" resultMap="resultMapTest" >
SELECT id id_, username username_, password password_, name name_, addr addr_ FROM user WHERE id = #{id}
</select>
<resultMap id="resultMapTest" type="user">
<id column="id_" property="id" />
<result column="username_" property="username" />
<result column="password_" property="password" />
<result column="name_" property="name" />
<result column="addr_" property="addr" />
</resultMap>
动态SQL
- Mybatis 对SQL 语句进行灵活的操作
if
- mapper.xml
<!-- 用于判断条件是否满足,userVO 是pojo User视图层对象,用于获取user对象 -->
<select id="findUserByUsingDTSql" parameterType="userVO" resultType="user">
SELECT * from user
<!--
where 可以自动去掉第一个and
-->
<where>
<if test="user != null">
<if test="user.sex != null">
AND sex = #{user.sex}
</if>
<if test="user.name != null">
And name LIKE #{user.name}
</if>
</if>
</where>
</select>
- 组成判断的sql片段
- 定义sql 片段
<!-- 定义sql 片段,id 唯一标识sql 片段
在sql 片段内部不要包括where
基于单表定义的sql 片段可复用性才高 -->
<sql id="if_test">
<if test="user != null">
<if test="user.sex != null">
AND sex = #{user.sex}
</if>
<if test="user.name != null">
And name LIKE #{user.name}
</if>
</if>
</sql>
- 使用sql 片段
<where>
<!-- 如果sql 片段在其他xml 文件内,则需要在前面加上namespace -->
<include refid="if_test"></include>
</where>
choose, wher, otherwise
<sql id="choose_test">
<choose>
<when test='user.sex == "男"'>
AND sex = "女"
</when>
<otherwise>
And 1 = 1
</otherwise>
</choose>
</sql>
trim, where, set
- where : 在sql 语句后添加条件
<where>
id = #{id}
</where>
- set : 在update 语句后添加设置语句
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
- trim : 切割sql 语句,将多余的关键词移除关于trim的使用
如实现 标签的作用
<where>
<if test="user != null">
<if test="user.sex != null">
AND sex = #{user.sex}
</if>
<if test="user.name != null">
And name LIKE #{user.name}
</if>
</if>
</where>
使用 实现
<trim prefix="WHERE" prefixOverrides="AND">
<if test="user != null">
<if test="user.sex != null">
AND sex = #{user.sex}
</if>
<if test="user.name != null">
AND name LIKE #{user.name}
</if>
</if>
</trim>
foreach
- 多个值的输入查询, sql 语句如下
SELECT * FROM user WHERE id = 1 OR id = 3 OR id = 5;
SELECT * FROM user WHERE id IN(1, 3, 5);
- 在mapper.xml 文件内配置如下
- 注意:
collection:输入对象的集合属性
item:每次遍历生成的对象
open:开始遍历拼接的串
close:结束遍历拼接的串
separator:遍历生成的两个对象之间需要拼接的串
<sql id="foreach_test">
<foreach collection="ids" item="id" open="AND(" close=")" separator="OR">
id = #{id}
</foreach>
</sql>
实现 SELECT * FROM user WHERE id IN(1, 3, 5);
<sql id="foreach_test">
<foreach collection="ids" item="id" open="id IN(" close=")" separator=",">
id = #{id}
</foreach>
</sql>