MyBatis笔记

MyBatis

引言

现有JDBC代码的问题:
大量代码的冗余
1. 获得conn
2. 创建pstm
3. 绑定参数的方法
4. 发送参数,执行sql
5. 处理结果集
6. 处理异常

在这里插入图片描述

特点

介绍: 数据持久层框架(DAO将数据持久化到数据库),实现DAO层的代码。对JDBC代码的封装。

特点:

1. 封装通用功能,简化代码,提高开发效率(获得conn,绑定参数,发送sql,处理异常,处理结果集)
2. sql放在配置文件中,提高sql可维护性。
3. 自带连接池功能
4. 自带缓存(提高查询效率) [重点]

第一个MyBatis程序

核心编程思想

1. 书写DAO接口
2. 开发Mapper文件
  SQL+绑定的参数===实现接口中的方法。

在这里插入图片描述

环境搭建

  1. 导入jar。

mybatis资料目录结构:

在这里插入图片描述>

1. 导入mybatis的jar
2. 导入oracle的驱动jar
3. 导入mybatis依赖lib的jar。
  1. 引入配置文件:
1. mybatis-config.xml(连接数据库相关的参数)
	driverClassName 驱动类名   oracle.jdbc.OracleDriver
	url						  jdbc:oracle:thin:@localhost:1521:xe
	username				  hr
	password				  hr
	连接池(POOLED)-------------配置连接池
2. XxxMapper.xml(相当于dao的实现类)

在这里插入图片描述>

  1. 初始化配置

MyBatis实现DAO编码

需求: 使用MyBatis实现DAO的方式,添加一个person信息?

1. 写DAO接口
public interface PersonDAO{
	void insert(Person person);
}
2. 书写Mapper文件(DAO的实现类)[sql+参数]
<insert id="实现接口的方法名" parameterType="参数中实体类型全类名">
	insert into t_person values(seq_person.nextval,#{属性名},#{sex},#{age},#{mobile},#{address})
</insert>
属性:
id: 实现的接口的方法名
parameterType: 参数类型(实体的全类名)
sql语中绑定参数: #{属性名}

在这里插入图片描述>```

  1. 注册管理mapper文件[在mybatis-config中配置]
<mappers>
		<!-- 注册管理所有的mapper文件 -->
		<mapper resource="com/baizhi/demo1/PersonDAOImpl.xml"></mapper>
</mappers>
resource: mapper文件相对于src的路径。
MyBatis使用的API[重点]
目的:
使用DAO:
1. 获得PersonDAO的对象。
2. 调用personDAO的方法。

常用的类:
sqlSession: 
	1. 获得dao接口的实现类的对象。
		XxxDAO dao = sqlSession.getMapper(接口.class);
	2. 相当于connection.[提交事务 关闭close,回滚事务]
SqlSessionFactory
	1. 获得sqlSession
		SqlSession session = sqlSessionFactory.openSession();
    2. 保存封装mybatis-config.xml配置文件。
    
SqlSessionFactoryBuilder: 读取配置文件
Resources: 获得读取配置文件的输入流。
步骤:
1. 获得mybatis-config的输入流
2. 读取mybatis-config的文件,构造成SqlSessionFactory
3. 通过SqlSEssionFactory获得SqlSession
4. 通过SqlSession获得DAO接口的对象
5. 调用方法测试。

单表操作

在这里插入图片描述

修改

  1. 书写PersonDAO接口,声明修改的方法
public interfafce PersonDAO{
    /**
       参数:
        id: 条件
        其他属性:  修改的新的值
    */
    public void update(Person person);
}
  1. 在Mapper通过标签实现方法
<update id="update" parameterType="Person的全类名">
	<!--sql,要绑定的参数-->
    update t_person set name = #{name},sex =#{sex},age=#{age},mobile=#{mobile},address=#{address} where id = #{id}
</update>
  1. 注册mapper文件。[一个接口,对应一个mapper文件,注册一次]

删除

  1. 定义dao接口的方法
  2. 书写mapper文件标签

在这里插入图片描述>3. 注册mapper文件。

限定参数绑定的名字
  1. 定义接口方法
public interface PersonDAO{
    public void delete(@Param("指定参数绑定使用的名字")Integer id);
}
  1. Mapper文件
<delete>
     delete from t_person where id = #{指定参数绑定使用的名字}
</delete>

查询

查询单个
  1. 书写DAO接口方法
public interface PersonDAO{
    Person selectById(Integer id);
}
  1. mapper文件实现该方法(sql 参数 对查询结果映射实体对象)

核心: sql 参数

映射核心思想:

如果查询结果的列名和要封装的实体的属性名一样,将查询结果自动封装实体对象。

<select id="selectById" parameterType="java.lang.Integer" resultType="查询结果的一行数据映射的实体对象类型">
	select id,name,sex,age,mobile,address from t_person where id = #{id}
</select>

在这里插入图片描述

  1. 注册mapper文件
ResultType的作用

作用: 映射查询结果的列封装成实体的属性

要求: 查询结果的列名必须和实体的属性名一致

在这里插入图片描述>

表列名和实体属性名不一致

通过sql 的as关键字,其别名方式,使查询结果的列名和实体的属性名一致。

查询多个

只需要明确单行数据映射的实体类型,

MyBatis会自动讲每个数据封装的每个实体放入list集合中。

  1. 书写DAO接口方法
public interface PersonDAO {
	List<Person> selectAll();
}
  1. 书写mapper文件中的标签
<select id="selectAll" resultType="rs的一行数据映射实体类型">
	select id,name,sex,age,mobile,address from t_person
</select>
  1. 注册mapper文件

多个参数(基本,包装,String)

方案一:

@Param给接口方法的参数起别名

public interface UserDAO {
	User selectByUsernameAndPassword(@Param("username")String username,@Param("password")String password);
}
select id,username,password from t_user where username = #{username} and password = #{password}

1542012114649.png)

方案二:

在这里插入图片描述>绑定参数通过#{参数的序号从0开始}

MyBatis的Mapper文件的sql书写 >或者 <

问题: 会发生转义

解决: 替换成转义字符

在这里插入图片描述## MyBatis注意事项总结

1. 增删改必须要提交事务。(事务自动开启,手动提交模式)
2. 参数绑定情况
	实体类型        							#{属性名}
    (单个参数)基本数据类型+包装类型+String		  #{随便}
    没有参数 
    多个参数
    
3. mapper文件转义字符。

MyBatisUtil的封装

MyBatis核心API

SqlSession:
	1. 相当于connection
		sqlSession.commit();//提交事务
		sqlSession.rollback();//回滚事务
		sqlSession.close();//将连接还回连接池。
	2. 获得XXxDAO接口的对象。
	轻量级对象,每次操作创建一个新的。
		比如: Action  SqlSession  Connection
SqlSessionFactory
	作用: 封装mybatis-config配置文件的信息。
	重量级对象,web应用只创建一个。对象的创建消耗资源。
package com.baizhi.demo4;

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 MyBatisUtil2 {
	private static SqlSessionFactory factory = null;
	private static ThreadLocal<SqlSession> tdl = new ThreadLocal<SqlSession>();
	static{
		InputStream is = null;
		try {
			is = Resources.getResourceAsStream("mybatis-config.xml");
			factory =  new SqlSessionFactoryBuilder().build(is);
		} catch (IOException e) {
			throw new RuntimeException("mybatis配置文件加载异常",e);
		}finally{
			if(is != null){
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	
	/**
	 * 获得SqlSession
	 * @throws IOException 
	 */
	public static SqlSession getSqlSession(){
		//1. 获得DAO对象  sqlSession
		SqlSession session = null;
		//从当前线程中获得session
		try {
			session = tdl.get();
			if(session == null){
				//当前线程如果没有sqlSEssion
				session = factory.openSession();
				//将session存入当前线程
				tdl.set(session);
			}
		} catch (Exception e) {
			throw new RuntimeException("获得SqlSession的异常",e);
		}
		return session;
	}
	
	/**
	 * 根据接口的类对象,获得dao的对象。
	 * @param clazz
	 * @return
	 */
	public static <T> T getMapper(Class<T> clazz){
		//获得sqlSession
		SqlSession session = getSqlSession();
		//调用session.getMapper(clazz);
		T t = session.getMapper(clazz);
		return t;
	}
	
	/**
	 * 提交事务+释放资源
	 */
	public static void commit(){
		//sqlSession.commit();
		try{
			SqlSession session = getSqlSession();
			session.commit();
		}catch(RuntimeException e){
			throw e;
		}finally{
			close();
		}
	}
	/**
	 * 回滚事务+释放资源
	 */
	public static void rollback(){
		try{
			SqlSession session = getSqlSession();
			session.rollback();
		}catch(RuntimeException e){
			throw e;
		}finally{
			//释放资源
			close();
		}
	}
	
	/**
	 * 释放session资源
	 */
	public static void close(){
		//session.close();
		SqlSession session = getSqlSession();
		if(session != null){
			session.close();
			//从当前线程中移除该session
			tdl.remove();
		}
	}
}

MyBatis使用技巧

配置文件提示

日志

MyBatis集成日志功能

mybatis运行期间的痕迹,通过控制台打印。

目的: 查看mybatis运行的痕迹,调试,验证。

使用

1. 导入日志的jar log4j.jar
2. 导入配置文件. log4j.properties   [必须放在src根目录]
补救工作:

在这里插入图片描述>

实体简化(别名)

简化实体的权限定名的书写

方案:

1. 给实体类的权限定名取别名。(mybatis-config配置文件)
   com.baizhi.demo1.Person      别名="Person"
   <typeAliases>
   	<typeAlias type="实体的全类名" alia="别名"></typeAlias>
   	<typeAlias></typeAlias>
   	<typeAlias></typeAlias>
   </typeAliases>
2. Mapper使用实体类名
	resultType="Person"

在这里插入图片描述

Mybatis参数绑定的底层原理[笔试]

1. 默认mapper文件绑定参数 #{xxx}
	① 底层使用的PreparedStatement对象。
	② 使用的 SQL ? [预编译]sql执行。和参数绑定。
	优点:
		预编译, 防止sql注入。
		预编译,相对效率高(一点)
   缺点: 只能绑定数据值. sql关键字,列 非数据无法绑定。
2. 使用sql字符串拼接绑定参数
	优点: 可以绑定任何内容。(关键词,列)
	缺点: sql注入。
	mybatis使用字符串拼接绑定参数: ${}

插入优化

JDBC的实现思路

在这里插入图片描述

MyBatis插入数据绑定id

<insert id='insert' parameterType="实体类型">
   <!--在insert语句之前,执行select序列的sql,为了给参数的id属性绑定值。-->
   <selectKey order="BEFORE"  resultType="java.lang.String" keyProperty="sql的执行结果绑定给参数的实体的哪个属性 id">
   	select seq_user.nextval from dual
   </selectKey>
	insert into t_user values(#{id},?,?)
</insert>

在这里插入图片描述

查询关系映射

需求: 将查询结果映射成实体对象。[MyBatis自动完成]

方案1:

resultType:
作用: 将查询结果(而不是表)映射成实体对象。
要求: 查询结果的列名和实体属性名保持一致。
问题: 如果 表的列名和实体属性名不一致,通过as其别名的方式,使查询结果列名和属性名一致。

在这里插入图片描述>

方案二:

给MyBatis定义映射关系
ResultMap: 明确告知mybatis 实体的属性名和查询结果的列名的对应关系。

在这里插入图片描述

Struts2+MyBatis整合

代码整合思路

SM整合编码思路

编码思路梳理

需求: 登录功能?

  1. 搭建环境
    导入strtus2相关的jar
    导入mybatis相关的jar
    数据库的驱动jar
    注意: 处理jar包冲突
    导入struts和myatis的配置文件
    初始化配置(struts2核心控制器,mybatis-config的数据库连接参数)

    工具类导入: MyBatisUtil JdbcUtil 配置文件
    功能开发步骤

  2. 实体

  3. DAO接口 XxxDAOImpl.xml(Mapper文件) 注册mapper文件

  4. Service业务功能的接口 实现类

  5. Action控制器

  6. JSP页面

关联关系操作

一对多

实例需求: 员工(多)和部门(1)

(站在员工): 一个员工对应一个部门(1对1)

(站在部门): 一个部门对应多个员工(1对多)

表设计

核心思想:

  1. 表示表与表之间的关系。
  2. 1个员工对应1个部门,1个部门对应多个员工

在这里插入图片描述

设计1 和 n的表关系
原则
1. 将外键添加在n的一方。
--## 部门表
   create table t_dept(
   id varchar2(36) primary key,
   name varchar2(50)
);
--## 员工表
create table t_emp(
   id varchar2(36) primary key,
   name varchar2(50),
   age number(3),
   salary number(10,2),
   dept_id references t_dept(id)
);
重要:
添加数据,先添加没有外键的数据(部门信息),再添加存在外键的数据(员工信息)



实体设计
1. 在实体中添加关系属性,来表示实体之间的关系(对应表数据的关系)
2. 在N的一方添加1的一个关系属性。
3.1的一方添加N的一个List的关系属性

在这里插入图片描述

DAO(MyBatis如何查询两张表信息)
需求1:查询员工信息(工号,名字,年龄 , 薪资,所属部门的编号和名称)根据员工工号?
DAO接口
//根据id查询员工信息  返回值Emp(员工信息+部门信息)
//特点:查询员工,对应1个部门
Emp selectById(String id);
Mapper文件
1. SQL
select e.id,e.name,e.age,e.salary,d.id,d.name
       from 
          t_emp e left join t_dept d on e.dept_id = d.id
       where e.id = '5';
2. 参数
3. 将查询结果映射成一个实体对象

使用ResultMap映射1对1的关系。

特点: 如果关系属性是1----<association></association>

在这里插入图片描述

需求2:

需求2:根据id查询部门信息,及其内部的所有员工信息?

DAO接口方法
public Dept selectById(String id);

Mapper文件中
1. SQL:
select d.id,d.name,e.id as eid,e.name as ename,e.age as eage,e.salary as esalary
from 
     t_dept d left join t_emp e on d.id = e.dept_id
     
where d.id = ?;
2. 参数绑定
3. 结果映射

ReusultMap映射集合关系属性
在这里插入图片描述

1对1关系

需求: 
学生电脑管理系统
库表设计

表示1对1的关系

1. 添加外键(那张表添加都可以)
   ① 从业务的角度分析,后添加的数据对应的表。
   ② 该表叫做副表(子表),添加外键。
2. 外键列约束  unique  唯一,不能重复

在这里插入图片描述

实体设计

互相保留彼此的一个关系属性

在这里插入图片描述

DAO设计

需求: 根据学生id查看学生信息(名字,年龄,手机号,使用的电脑编号,电脑名称)?

DAO接口设计

public interface StudentDAO{
   //根据学生id查看学生信息(名字,年龄,手机号,使用的电脑编号,电脑名称)?
   public Student selectById(String id);
}

Mapper文件

1. sql语句
2. 参数
3. 映射结果集(Student)

在这里插入图片描述

多对多

需求:

在这里插入图片描述

库表设计

设计原则:

n对n的关系建表
原则: 创建第三个关系表。
--学生表
create table t_student(
      id varchar2(36) primary key,
      name varchar2(50),
      age number(3),
      mobile varchar2(11)
);
--课程表
create table t_course(
      id varchar2(36) primary key,
      name varchar2(50)
);
--选课表
create table t_stu_course(
    sid references t_student(id),
    cid references t_course(id),
    primary key(sid,cid)-- 【联合sid和cid作为主键,非空 联合唯一】如果一个学生只能选择一个课程一次。
);
注意:
   如果产品需求中要求一个学生,选择一个课程,只能选择一次。
   解决办法: 使选课关系表中,两个外键联合作为主键。

在这里插入图片描述

实体设计
设计原则:
互相保留对方的一个集合属性即可。

在这里插入图片描述

DAO设计
1. 根据id查询学生学号,姓名,年龄,手机号,课程编号,课程名称?
   本质: 1对n
DAO接口

public interface StudentDAO{
	public Student selectById(String id);
}
Mapper文件
<resultMap>
	主属性 id标签
	一般属性 result标签
	关系属性(list集合关系属性) collection标签。
</resultMap>
<select>
	sql: 以学生表为主,关联关系表和课程表?
</select>

MyBatis高级特性

动态sql

复用sql语句

案例: 复用sql的列名:

1. 定义sql片段
	<sql id="Xxx_column">被复用sql片段</sql>
2. 引用sql片段
	<include refid="Xxx_column"></include>
优点:
	1. 简化sql书写。
	2. 提高sql的可维护性。

在这里插入图片描述

(Where+if)动态sql简化查询

封装查询参数

package com.baizhi.demo2;
/**
* 所有可能的查询参数
* 学校
* 专业
* 状态
* 同时
* id
* 查询对象: 封装了所有可能的查询参数
* @author Administrator
*
*/
public class PersonQuery {
	private String id;
	private String school;
	private String professional;
	private Integer status;
   ....

DAO的简化

将页面所有可能的查询参数, 封装成XxxQuery对象。

在这里插入图片描述
Mapper的selectwhere条件设计

where标签+if标签

核心: 调用dao方法传入的参数不同,决定了sql的条件不同
动态sql: 一个sql标签,由于传入参数不同,实际执行的sql语句也不同。

在这里插入图片描述

调用DAO:

1. 将查询条件放入XxxQuery对象中
2. dao.selectxxx(query);

#在这里插入图片描述

update+if 动态修改
set标签:
  ① 替代set关键字
  ② 自动忽略修改的列后面多余的 逗号。
if标签:
	test="dao方法的参数属性是否有值"
语法:
	<if test="参数的属性名 != null">
		...修改sql
	</if>

在这里插入图片描述#### 动态sql删除

DAO接口的方法

void delete(@Param("ids")String[] ids);

Mapper文件

在这里插入图片描述

补充trim [了解]
作用: 
1. 可以替代任何任何关键字(where set)
2. 可以忽略任何特殊字符(and ,)

在这里插入图片描述

在这里插入图片描述

缓存

现有数据查询的问题?

  1. 数据库的数据,来源于数据库的物理硬盘(150M/s~450M/s),速度慢?
  2. 数据获取,每次都要经过DB–>Tomcat的网络传输。,网络传输IO,降低速度?

解决思路

  1. 内存,读取速度(10000M/s),速度快于物理硬盘。
  2. 将数据临时存放在tomcat本地,再次获取数据,无需通过网络从DB传输给java。

缓存

概念:
	数据的临时存放空间。
特点:
	数据会被临时存放在内存中。(内存)
	1. 可以将查询结果临时存放在缓存中。
	2. 数据获取,从第二次开始,从缓存中获取。
	3. 服务器内部缓存(JVM内部),每次获得数据,无需通过网络传输。
作用:
	1. 提高查询效率
	2. 降低的数据库的访问压力
		数据库的硬件配置固定,大量查询,会侵占数据库的资源(CPU 内存)
		数据库的可同时提供的连接数有限,大量查询,会侵占数据库的连接资源.

缓存核心架构:
在这里插入图片描述

MyBatis缓存

一级缓存

概念: SqlSession级别的缓存

核心思想:

在这里插入图片描述

特点:

  1. 每个sqlSession会有独立的缓存空间。
  2. SqlSession查询数据,默认放入缓存中。
  3. 第二次查询,sqlSession直接从缓存中获得数据,不会发送sql语句。
  4. 生命周期: 一个事务过程。【sqlSession.commit().一级缓存就会被清空】

应用

没什么用

二级缓存

 概念:
 1: 全局缓存
 2: SqlSessionFactory级别缓存.
 3: 多个SqlSession可以共享的。

工作机制:

1. 第一次查询,数据会从数据库(物理硬盘)获得数据,并且,通过网络传输给java。
2. 将第一次的查询结果,放入全局缓存[二级缓存]3.2+次以后,每次查询都从缓存获取。[从tomcat的jvm的内存中获得数据,避免从DB网络传输数据。]

在这里插入图片描述

二级缓存的使用步骤
  1. 二级缓存MyBatis默认自动开启了
如果mybatis版本,没有默认开启二级缓存
<settings>
	<setting name="cacheEnabled" value="true"/>
</settings>
  1. 被缓存的数据对象类型要可序列化
public class xxxx implements Serializable{
    ...
}
  1. 在使用二级缓存的查询sql所在的Mapper中添加
<mapper>
	<cache></cache>
</mapper>
作用: 当前mapper文件下所有的select标签,查询结果都会默认自动放入到缓存中。
  1. 缓存的使用
查询工作机制: 
1. 每次查询先从缓存中获得数据,如果获得,直接返回,不会发送sql,
2. 如果没有从缓存获得需要的数据,再发送sql,从数据库获得数据,会将查询结果放入缓存中。

深入二级缓存

特点
1. 开启了二级缓存的mapper文件中的select标签语句默认会使用缓存。(先从缓存中获取,获得到,返回,获得不到,从db获得,放入缓存)
  先从select语句所对应的namespace的缓存空间中,获得数据,如果获得,返回,如果没有,则发送sql查询数据,并将查询结果放入sql所对应的namespace的缓存空间。
2. mapper文件中的 DML标签insert update delete ,会默认清空缓存。[为了保证缓存中数据一致性]
	清空dml语句所在mapper文件,对应的namespace的缓存空间。
3. session.commit();当前session查询结果,才会放入二级缓存中。[空一级缓存]

缓存实现原理(源码)

  1. MyBatis二级缓存划分规则:
1. MyBatis二级缓存,是以(Mapper的)namespace进行划分
2. SQL标签 (select)操作的是sql所在的mapper文件的namespace对应的缓存空间。

在这里插入图片描述

  1. MyBatis缓存的源码实现?

每个namesapce对应的缓存源码实现?

1. mybaits的namesopace的缓存实现,本质HashMap
2. select语句的查询结果,是如果放入缓存
	key: "namespaceid:执行的sql语句:实际传入的查询参数"
	value: 查询结果的返回值。

缓存的应用场景

原则:该数据的查询次数远远多于修改的次数

比如:

类别信息、登录的用户信息 适合放入缓存。
最新动态、新书、个性推荐.不适合放入缓存。

在这里插入图片描述

注意:

MyBatis二级缓存属于服务器内部缓存。
如果缓存数据过多,会挤占tomcat服务器内部(JVM)内部代码运行本身需要的内存空间,导致代码运行异常。

Ehcache缓存整合[重点]

简介

1. Ehcache 专职负责实现缓存。
2. Ehcache 分布式缓存。
3. Ehcache支持将数据序列化硬盘上(支持,不太可靠)
4. 服务器内部缓存(jvm进程内缓存)

Ehcache集成MyBatis(替换mybatis自带的缓存)

1. 导入Ehcache的jar,导入ehcache集成MyBatis的jar
	ehcache-core-2.6.5.jar
	mybatis-ehcache-1.0.2.jar
2. 在mapper中使用缓存是,指定使用Ehcache的缓存实现类
	<cache type="Ehcache实现类的全类名"></cache>

配置文件[了解]

<!--  
      maxElementsInMemory:缓存最大的对象个数。  
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。  
      overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。  
      maxElementsOnDisk:硬盘最大缓存个数。  
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。
      	数据淘汰策略。
      	默认策略是LRU  (last resently used 最近最少使用)。  新闻(每天更新)
      	你可以设置为FIFO(first in first out先进先出)
      	或是LFU(least requently used 较少使用)-->  
   <defaultCache
           maxElementsInMemory="10000"  
           eternal="false"  
           overflowToDisk="true"
           maxElementsOnDisk="10000000"  
           memoryStoreEvictionPolicy="LRU"  
   />
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值