MyBatis框架
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架(DAO层)。
MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索
JDBC代码 : JDBC编程六步骤
参数的手工设置: JDBC参数动态化 SQL语句中?的替换
结果集的检索:ORM思想中对于ResultSet结果集的处理
思想
只关注持久层中最为核心的内容 ---> SQL语句
将所有的代码全部封装只留下配置文件 mapper.xml文件
interface UserDAO{
List<User> queryUser();
void insert(User user);
}
mapper.xml 就相当于 JDBC中UserDAOImpl.java
<mapper 声明实现的是某一个具体的DAO接口 = UserDAO>
<select id="queryUser">
select * from t_user;
</select>
<insert id="insert">
insert into t_user values(对象中的数据);
</insert>
</mapper>
一、第一个程序
1、环境搭建
1.1 导入jar包
1. mybatis 核心jar包 mybatis-3.2.2.jar
2. 三方jar包 mybatis源码所依赖的其他java类 zip----> lib文件夹中
3. 数据库驱动jar ojdbc.jar
1.2 准备配置文件
1、log4j.properties 日志
将mybatis运行的流程日志全部进行打印 可选 : 打印到控制台 或者 写入文本中。
位置: src 根目录下
log4j日志如果使用在Web项目中,需要生效必须部署在服务器上。
Web项目中使用Test测试是看不到运行日志的。
2、mybatis-config.xml
mybatis核心配置文件
2.1 对于数据库连接的访问参数配置
2.2 对于mapper.xml文件的注册(声明)
位置: 随意的 建议 src 根目录下
3、Mapper.xml 只会实现一个DAO接口
替换DAOImpl.java实现类的 和DAO接口的数量 一一对应关系
位置: 随意
1.3 初始化配置
<configuration>
<!-- JDBC数据连接的初始化配置 -->
<environments default="oracle">
<environment id="oracle">
<!-- 使用传统的JDBC方式管理事务 -->
<transactionManager type="JDBC" />
<!-- POOLED MyBATIS中的连接池方式 可以进行后续的替换 c3p0 -->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe">
</property>
<property name="username" value="hr"></property>
<property name="password" value="hr"></property>
</dataSource>
</environment>
</environments>
<!-- 声明注册项目中已经完成编码的mapper.xml文件的路径-->
<mappers>
<mapper resource="Mapper.xml文件的路径"></mapper>
</mappers>
</configuration>
2、编码
实体类:
User 属性 id 、 name 、password
接口:
public interface UserDAO {
List<User> queryAll();
}
Mapper.xml
<!--
namespace: 指定当前mapper.xml管理的接口 全限定名
注意:一个mapper.xml只能管理一个接口
-->
<mapper namespace="com.baizhi.dao.UserDAO">
<!-- id="方法名" resultType=" 返回结果类型 集合中的泛型的全限定名" -->
<select id="queryAll" resultType="com.baizhi.entity.User">
select * from t_user;
</select>
</mapper>
注意: 完成mapper.xml的开发之后还需要去mybatis-config.xml中进行注册
mybatis-config.xml文件
<!-- 声明注册项目中已经完成编码的mapper.xml文件的路径-->
<mappers>
<mapper resource="com/baizhi/mapper/UserDAOMapper.xml"></mapper>
</mappers>
3、测试
@Test
public void testQueryAll()throws Exception{
//如何获取UserDAO的实现类结果呢? UserDAO userDAO = new UserDAOImpl();
/* 1. Resources 用来读取mybatis核心配置文件
* 2. SqlSessionFactory SqlSession工厂 (连接工厂)
* 3. SqlSeesion对象 为MyBATIS技术中的连接对象 相当于JDBC中的connection
* SqlSeesion.getMapper() ----> 获取DAO的实现类对象
*/
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
List<User> queryAll = userDAO.queryAll();
for (User user : queryAll) {
System.out.println(user);
}
is.close();
sqlSession.close();
}
DML操作
接口方法:void deleteUser(int id);
Mapper.mxl配置文件:
<!-- 方法中参数的类型 parameterType="int" 可以省略 针对八种基本类型以及String的时候-->
<delete id="deleteUser" >
delete from t_user where id = #{id}
</delete>
Test测试类:
//读取mybatis核心配置文件
InputStream resourceAsStream =
Resources.getResourceAsStream("mybatis-config.xml");
//获取工厂 sqlSession的工厂
SqlSessionFactory sessionFactory =
new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取sqlsession对象
SqlSession sqlSession = sessionFactory.openSession();
//UserDAO userDAO =
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
userDAO.deleteUser(1);
//默认事务选择rollback回滚 必须手动进行提交
sqlSession.commit();
resourceAsStream.close();
sqlSession.close();
二、参数传递
1、如果方法中只有一个参数
void deleteUser(int id);
#{取值可以随便写} :#{a} 标准写法: #{0} 建议和参数名称对应:#{id}
2、如果方法中有多个参数
2.1 使用原始的数组 默认
User queryUser(String name ,String password);
#{按照数组下标} mybatis会将参数列表封装到数组中
name = #{0} and password = #{1}
2.2使用参数绑定 @Param注解
User queryUser(@Param(“name”)String name,@Param(“p”)String password);
select * from t_user where name = #{name} and password = #{p}
3、使用Map集合
// Map 集合 key=value name=123 password=123
User queryUserMap(Map<String,Object> maps);
xml: #{map集合中的key}
<select id="queryUserMap" resultType="com.baizhi.entity.User">
select * from t_user where name = #{name} and password = #{password}
</select>
使用:
HashMap<String, Object> hashMap = new HashMap<String,Object>();
hashMap.put("name", "123");
hashMap.put("password", "123");
User queryUser = userDAO.queryUserMap(hashMap);
4、用户自定义对象
<!-- 方法中参数的类型 parameterType="全限定名" 如果是用户自定义对象一定需要添加 -->
<insert id="insertUser" parameterType="com.baizhi.entity.User">
<!-- id name password 为用户自定义对象的属性 -->
insert into t_user values(#{id},#{name},#{password})
</insert>
注意: 查询操作中 如果接口的方法返回值是一个对象 则SQL语句的查询结果必须也是一个。
org.apache.ibatis.exceptions.TooManyResultsException:
Expected one result (or null) to be returned by selectOne(), but found: 2
5、使用序列生成ID
建议书写格式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G3XVJVRo-1594885695797)(E:\JavaWeb\Mybatis\笔记\1.png)]
<insert id="insertUser" parameterType="com.baizhi.entity.User">
insert into t_user values(user_seq.nextval,#{name},#{password})
</insert>
三、MyBatisUtil的封装
封装思想完全遵循 JDBCUtil
对于冗余代码的封装、执行效率(static{} 静态代码块)、线程绑定(ThreadLocal)
package com.baizhi.util;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
//冗余代码的封装
//效率
public class MyBatisUtil {
private static SqlSessionFactory sessionFactory;
private static ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();
static{
try {
InputStream resourceAsStream =
Resources.getResourceAsStream("mybatis-config.xml");
//获取工厂 sqlSession的工厂
sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
resourceAsStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//获取SqlSeesion 连接
public static SqlSession getSqlSeesion() throws IOException{
SqlSession sqlSession = tl.get();
if(sqlSession==null){
//获取sqlsession对象
sqlSession = sessionFactory.openSession();
tl.set(sqlSession);
}
return sqlSession;
}
//关闭资源 需要在关闭资源之前 先进行线程的解除绑定
public static void close(){
try {
SqlSession sqlSession = getSqlSeesion();
//线程接触绑定
tl.remove();
sqlSession.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void commit(){
try {
SqlSession sqlSession = getSqlSeesion();
sqlSession.commit();
//调用本类中的关闭方法
close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void rollback(){
try {
SqlSession sqlSession = getSqlSeesion();
sqlSession.rollback();
//调用本类中的关闭方法
close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
四、配置文件中的一些细节
1、实体类(entity)别名
User queryUser();
方法的返回值类型 : resultType="com.baizhi.entity.User"
void insertUser(User user);
方法的参数表类型 : parameterType="com.baizhi.entity.User"
<typeAliases>
<!-- type 实体类的权限定名称 alias 别名 实体类的类名 -->
<typeAlias type="com.baizhi.entity.User" alias="User"/>
</typeAliases>
<!-- resultType / parameterType = "User"-->
2、配置文件的参数化
将mybatis-config.xml中 跟数据库相关的配置参数书写在另一个配置文件中。
简化后续操作的复杂性
<!-- 读取jdbc相关配置信息的 文件 进行数据的填充需要使用 ${key} -->
<properties resource="jdbc.properties"></properties>
<!-- 具体的参数配置已经转移到另外一个更小的配置文件中 jdbc.properties -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
五、resultMap 结果映射
ORM 对象关系映射 数据库中表的列名 ----> java中对象的属性名称
如果表中的列名和实体类的属性名不对应呢?
Mapper.xml 中书写
<!-- type="需要进行属性和列名对应的实体类" -->
<resultMap type="User" id="UserResultMap">
<!-- 主键列 使用 id标签 剩下的列使用 result标签 -->
<!-- property="实体类属性" column="表的列名" -->
<id property="id" column="id"/>
<result property="username" column="name"/>
<result property="password" column="password"/>
</resultMap>
<select id="queryAll" resultMap="UserResultMap">
select * from t_user
</select>
六、表中的关联关系
1、 一对一关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6rH30fZK-1594885695803)(E:\JavaWeb\Mybatis\笔记\一对一.png)]
查询Pseron对象以及其身份信息 — 来源于两张表的结果集合
书写 ResultMap 手动完成结果映射
<resultMap type="Person" id="PersonResultMap">
<id property="p_id" column="p_id"/>
<result property="p_name" column="p_name"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="id" column="id"/>
<!-- association Person实体类中有个一属性是用户自定义属性
切在数据查询的时候来源于另外一张表 -->
<!-- property="类中的属性名称" javaType="属性的类型" -->
<association property="idCart" javaType="IDCart">
<id property="id" column="id"/>
<result property="address" column="address"/>
<result property="name" column="name"/>
</association>
</resultMap>
<select id="queryAll" resultMap="PersonResultMap">
select * from Person p
inner join IDCart i
on p.id = i.id
</select>
Struts+MyBATIS整合 问题 ----- jar包冲突 commons-logging-1.1.3.jar/ javassist-3.17.1.GA.jar
建议 使用版本较高的jar包
2、一对多
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKy1OSo2-1594885695806)(E:\JavaWeb\Mybatis\笔记\一对多.png)]
<resultMap type="Cla" id="ClaResultMap">
<id property="cla_id" column="cla_id"/>
<result property="address" column="address"/>
<!-- 实体类属性是一个完整的集合对象 -->
<!-- property="实体类中属性的名称" ofType="集合中的泛型" -->
<collection property="stus" ofType="Stu">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="cla_id" column="cla_id"/>
</collection>
</resultMap>
3、多对多
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28RI0sEZ-1594885695813)(E:\JavaWeb\Mybatis\笔记\多对多.png)]
需要引入第三张表去进行关联关系的拆分
关系表不需要主键存在 — 两个外键可以视为 联合主键
实体类设计 每个实体类其中的一个属性是另一个是实体类的集合
七、动态SQL
1、SQL片段
<sql id="T_USER_SELECT">
id,name,password
</sql>
<select id="queryAll" resultType="User" >
select <include refid="T_USER_SELECT"/>
from t_user
</select>
2、where 判断
<select id="queryUser" parameterType="User" resultType="User">
select * from t_user
<where>
<if test="id!=null">
id=#{id}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
1、会根据实体类中具体的属性个数来生成对应的查询条件判断
2、八种基本类型需要进行区分 int 默认值是0 Integer 默认值才是 null
<select id="queryUser" parameterType="User" resultType="User">
select * from t_user
<!-- prefix="where" 标识 trim 替换where 标签
prefixOverrides="and" 忽略第一个and 关键字 -->
<trim prefix="where" prefixOverrides="and">
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="password!=null">
and password=#{password}
</if>
</trim>
</select>
3、set 修改
如果列数据为null 则不进行修改,减少每一次修改对正行数据进行更新的问题。
<update id="updateUser" parameterType="User">
update t_user
<set>
<if test="name!=null">
name=#{name},
</if>
<if test="password!=null">
password=#{password}
</if>
</set>
where id=#{id}
</update>
<update id="updateUser" parameterType="User">
update t_user
<!-- suffixOverrides="," 和 where中的不一样!!!!!! -->
<trim prefix="set" suffixOverrides=",">
<if test="name!=null">
name=#{name},
</if>
<if test="password!=null">
password=#{password}
</if>
</trim>
where id=#{id}
</update>
4、foreach
<delete id="delete">
delete from t_user
where id in
<!-- 集合 ids open( #{id} , #{id},#{id} )close-->
<foreach collection="list" open="(" item="id" separator="," close=")">
#{id}
</foreach>
</delete>
collection = "集合的类型"
item="当前的遍历对象" #{item属性值}
八、缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOMCE6HM-1594885695817)(E:\JavaWeb\Mybatis\笔记\缓存.png)]
配置
1. 声明缓存的使用
开启二级缓存 mybatis-config.xml中
<!-- 开启MyBatis二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2. 使用标记缓存 Mapper.xml 文件中
<cache/>
3. 长时间为进行二次查询的数据会 唤回数据库(销毁)
所有的实体类需要实现对象序列化
注意:
1、 如果发生了数据的修改(DML操作 ----> commit)
MyBatis会自动清空缓存区中的数据
2、 缓存越多越好吗?
时间 成本
3、 查询操作一定要关闭连接 sqlSession
作业字段
用户表 用来登录注册 账号/密码/邮箱
省份表 ID 省份 标签
景点表 ID 景点 印象图 旺季时间 旺季门票 淡季门票 简介 外键 省份的ID
添加景点: 选择其对应的省份 省份信息需要动态的从数据库中获取
景点个数: 需要从景点表中动态的获取
1. 声明缓存的使用
开启二级缓存 mybatis-config.xml中
<!-- 开启MyBatis二级缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2. 使用标记缓存 Mapper.xml 文件中
<cache/>
3. 长时间为进行二次查询的数据会 唤回数据库(销毁)
所有的实体类需要实现对象序列化
注意:
1、 如果发生了数据的修改(DML操作 ----> commit)
MyBatis会自动清空缓存区中的数据
2、 缓存越多越好吗?
时间 成本
3、 查询操作一定要关闭连接 sqlSession
作业字段
用户表 用来登录注册 账号/密码/邮箱
省份表 ID 省份 标签
景点表 ID 景点 印象图 旺季时间 旺季门票 淡季门票 简介 外键 省份的ID
添加景点: 选择其对应的省份 省份信息需要动态的从数据库中获取
景点个数: 需要从景点表中动态的获取