一、mybatis是什么
- MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象映射成数据库中的记录.
二、为什么要用mybatis
- mybatis是一个半自动化的持久化层框架
- 使用jdbc时SQL语句夹杂在代码当中,耦合度高导致硬编码内伤
- 维护过程中修改sql语句经常发生,导致维护难度大
- sql和Java编码分开,功能边界清晰,一个专注与业务,一个专注于数据
三、去哪里找mybatis
- 链接:https://github.com/mybatis/mybatis-3/
四、mybatis怎么用
4.1Java项目结构的整体把控
4.1.1三层架构
- 界面层:和用户打交道,实现和用户的交互,接受用户的请求参数,显示处理结果(jsp\html\servlet\thymeleaf)
- 业务逻辑层:专注于业务的是实现,对数据进行处理,调用数据库
- 数据访问层:访问数据库,对数据库进行增删改查等
三层对应的包:
- 界面层controller(servlet)
- 业务逻辑层service
- 数据访问层dao
三层中类的交互
- 用户使用的界面层–>业务逻辑层–>数据访问层–>数据库
三层对应的框架
- 界面层—servlet—springMVC(框架)
- 业务逻辑层—service–spring(框架)
- 数据访问层–dao–mybatis(框架)
4.1.2框架
框架就是一个模板,规定好了一些条款内容在加入自己实际个性化的东西来满足业务的需求
框架就是一个模块,框架中写好的功能可以直接拿来用,或者可以将框架中的功能进行再加工实现实际业务需要的功能
4.2mybatis提供了哪些功能
- 提供了创建Connection ,Statement, ResultSet的能力 ,不用开发人员创建这些对象了
- 提供了执行sql语句的能力, 不用你执行sql
- 提供了循环sql, 把sql的结果转为java对象, List集合的能力
while (rs.next()) {
Student stu = new Student();
stu.setId(rs.getInt("id"));
stu.setName(rs.getString("name"));
stu.setAge(rs.getInt("age"));
//从数据库取出数据转为 Student 对象,封装到 List 集合
stuList.add(stu);
}
4.提供了关闭资源的能力,不用你关闭Connection, Statement, ResultSet
开发人员做的是: 提供sql语句
最后是: 开发人员提供sql语句–mybatis处理sql—开发人员得到List集合或java对象(表中的数据)
4.3mybatis主要类的介绍
1) Resources: mybatis中的一个类, 负责读取主配置文件
InputStream in = Resources.getResourceAsStream("mybatis.xml");
2)SqlSessionFactoryBuilder : 创建SqlSessionFactory对象,
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);
3)SqlSessionFactory : 重量级对象, 程序创建一个对象耗时比较长,使用资源比较多。
在整个项目中,有一个就够用了。
SqlSessionFactory:接口 , 接口实现类: DefaultSqlSessionFactory
SqlSessionFactory作用: 获取SqlSession对象。
SqlSession sqlSession = factory.openSession();
openSession()方法说明:
- openSession() :无参数的, 获取是非自动提交事务的SqlSession对象
- openSession(boolean): openSession(true) 获取自动提交事务的SqlSession.
- openSession(false) 非自动提交事务的SqlSession对象
4)SqlSession:
SqlSession接口 :定义了操作数据的方法 例如 selectOne() ,selectList() ,insert(),update(), delete(), commit(), rollback()
SqlSession接口的实现类DefaultSqlSession。
使用要求: SqlSession对象不是线程安全的,需要在方法内部使用,
在执行sql语句之前,使用openSession()获取SqlSession对象。
在执行完sql语句后,需要关闭它,执行SqlSession.close(). 这样能保证他的使用是线程安全的。
4.4使用mybatis的步骤
-
动态代理: 使用SqlSession.getMapper(dao接口.class) 获取这个dao接口的对象
-
传入参数: 从java代码中把数据传入到mapper文件的sql语句中。
1)parameterType : 写在mapper文件中的 一个属性。 表示dao接口中方法的参数的数据类型。
例如StudentDao接口
public Student selectStudentById(Integer id)
2) 一个简单类型的参数:
简单类型: mybatis把java的基本数据类型和String都叫简单类型。
在mapper文件获取简单类型的一个参数的值,使用 #{任意字符}
接口:public Student selectStudentById(Integer id)
mapper:select id,name, email,age from student where id=#{studentId}
3)多个参数,使用@Param命名参数
接口 public List<Student> selectMulitParam(@Param("myname") String name, @Param("myage") Integer age)
使用 @Param("参数名") String name
mapper文件:
<select>
select * from student where name=#{myname} or age=#{myage}
</select>
4 ) 多个参数,使用java对象
语法 #{属性名}
vo: value object , 放一些存储数据的类。比如说 提交请求参数, name ,age
现在想把name ,age 传给一个service 类。
vo: view object , 从servlet把数据返回给浏览器使用的类,表示显示结果的类。
pojo: 普通的有set, get方法的java类。 普通的java对象
Servlet --- StudentService( addStudent( MyParam param) )
entity(domain域): 实体类, 和数据库中的表对应的类,
5)#和$
select id,name, email,age from student where id=#{studentId}
# 的结果: select id,name, email,age from student where id=?
select id,name, email,age from student where id=${studentId}
$ 的结果:select id,name, email,age from student where id=1001
String sql="select id,name, email,age from student where id=" + "1001";
使用的Statement对象执行sql, 效率比PreparedStatement低。
$:可以替换表名或者列名, 你能确定数据是安全的。可以使用$
# 和 $区别
1. #使用 ?在sql语句中做站位的, 使用PreparedStatement执行sql,效率高
2. #能够避免sql注入,更安全。
3. $不使用占位符,是字符串连接方式,使用Statement对象执行sql,效率低
4. $有sql注入的风险,缺乏安全性。
5. $:可以替换表名或者列名
-
mybatis的输出结果
mybatis执行了sql语句,得到java对象。1)resultType结果类型, 指sql语句执行完毕后, 数据转为的java对象, java类型是任意的。
resultType结果类型的它值 1. 类型的全限定名称 2. 类型的别名, 例如 java.lang.Integer别名是int
处理方式:
1. mybatis执行sql语句, 然后mybatis调用类的无参数构造方法,创建对象。
2. mybatis把ResultSet指定列值付给同名的属性。
<select id="selectMultiPosition" resultType="com.bjpowernode.domain.Student">
select id,name, email,age from student
</select>
//对等的jdbc
ResultSet rs = executeQuery(" select id,name, email,age from student" )
while(rs.next()){
Student student = new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"))
}
2)定义自定义类型的别名
- 在mybatis主配置文件中定义,使定义别名
- 可以在resultType中使用自定义别名
3)resultMap:结果映射, 指定列名和java对象的属性对应关系。
- 你自定义列值赋值给哪个属性
- 当你的列名和属性名不一样时,一定使用resultMap
resultMap和resultType不要一起用,二选一
4.5动态sql
动态sql是指sql语句是动态变化的,不同的条件获取不同的sql语句,主要是where部分变化
- 动态sql的实现:使用<if>, <where>,<foreach>标签
- <if>是判断条件的,
语法
<if test="判断java对象的属性值">
部分sql语句
</if>
-
<where> 用来包含 多个的, 当多个if有一个成立的, 会自动增加一个where关键字,
并去掉 if中多余的 and ,or等。 -
<foreach>循环java中的数组,list集合的。 主要用在sql的in语句中。
学生id是 1001,1002,1003的三个学生
select * from student where id in (1001,1002,1003)
public List<Student> selectFor(List<Integer> idlist)
List<Integer> list = new ...
list.add(1001);
list.add(1002);
list.add(1003);
dao.selectFor(list)
<foreach collection="" item="" open="" close="" separator="">
#{xxx}
</foreach>
collection:表示接口中的方法参数的类型, 如果是数组使用array , 如果是list集合使用list
item:自定义的,表示数组和集合成员的变量
open:循环开始是的字符
close:循环结束时的字符
separator:集合成员之间的分隔符
- sql代码片段, 就是复用一些语法
步骤
1.先定义 sql语句, 表名,字段等
2.再使用,
4.6mybatis配置
- 数据库属性配置文件
在resources目录中定义一个属性配置文件, xxxx.properties ,例如 jdbc.properties在属性配置文件中, 定义数据,格式是 key=value
key: 一般使用 . 做多级目录的。
例如 jdbc.mysql.driver , jdbc.driver, mydriver
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql//.....
jdbc.username=root
jdbc.password=123456
在mybatis的主配置文件,使用 < property> 指定文件的位置
在需要使用值的地方, ${key}
- mapper文件,使用package指定包路径
<mappers>
<!--第二种方式: 使用包名
name: xml文件(mapper文件)所在的包名, 这个包中所有xml文件一次都能加载给mybatis
使用package的要求:
1. mapper文件名称需要和接口名称一样, 区分大小写的一样
2. mapper文件和dao接口需要在同一目录
-->
<package name="com.bjpowernode.dao"/>
</mappers>
五、示例演示
- 建立表的实体类
package pojo;
/**
* @Author Warrior
* @Date 2022/2/22 13:03
* @Description : user实体类
* @Since version-1.0
*/
public class t_user {
private Integer id;
private String username;
private String password;
private String email;
public t_user() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "t_user{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
- 建立dao接口
package dao;
import pojo.t_user;
import java.util.List;
/**
* @Author Warrior
* @Date 2022/2/22 13:06
* @Description : user实体的dao接口
* @Since version-1.0
*/
public interface UserDao {
/**
* 获取所有的用户信息
* @return
*/
List<t_user> selectUsers();
}
- dao接口和mapper映射
<?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">
<!--映射的dao的全路径名-->
<mapper namespace="dao.UserDao">
<!--对应的方法-->
<select id="selectUsers" resultType="pojo.t_user">
select id,username,password,email from t_user
</select>
</mapper>
- mybatis的主配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 记录日志信息-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<environments default="mysql">
<!-- 一个数据库信息的配置,id:唯一值-->
<environment id="mysql">
<!-- 事务类型:type:jdbc(表示使用jdbc中的connect对象的commit,rollback做事务处理)-->
<transactionManager type="JDBC"/>
<!-- dataSource:数据源
type表示数据源的类型,pooled表示连接池-->
<dataSource type="POOLED">
<!-- -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/book"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- sql映射文件的位置,一个标签指定一个文件位置,编译后的类路径(target/classes下的文件)-->
<!-- name:包名,这个包中的所有mapper.xml一次都能加载
使用package的要求:
1.mapper文件名称和dao接口的名称必须完全一样,包括大小写
2.mapper文件和dao接口必须在同一个目录
-->
<!--mybatis映射的所有mapper-->
<mapper resource="dao/UserDao.xml"/>
</mappers>
</configuration>
- 测试与数据库的交互情况
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import pojo.t_user;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class mybatisTest {
@Test
public void testStart() throws IOException {
//配置文件
String config = "mybatis.xml";
//读取配置文件
InputStream inputStream = Resources.getResourceAsStream(config);
//创建sqlsessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//获取session对象执行sql语句
SqlSession Session = factory.openSession();
List<t_user> users = Session.selectList("dao.UserDao.selectUsers");
users.forEach(user-> System.out.println(user));
Session.close();
}
}
mybatis就是将对数据库的操作抽取出mapper文件,将sql语句与Java代码分离出来,更好维护代码。
使用的步骤:
1.建立与数据库映射的表格实体类
2. dao的接口里写对数据库操作的抽象方法
3. mapper类中写清楚此mapper类映射的dao类的全类名,将相应方法的sql写全
4. 调用service中的方法实现与数据库的交互
六、mybatis分析
传统的Dao开发方式,通过自定义dao接口的实现类来通过sqlsession执行sql语句。所以,MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对DB 进行操作。
这种对 Dao 的实现方式称为 Mapper 的动态代理方式。Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的
6.1Dao代理实现CURD
- getMapper()获取代理对象
SqlSession session = factory.openSession();
StudentDao dao = session.getMapper(StudentDao.class);
- 使用获取到的代理对象执行sql语句
public void testSelect() throws IOException {
final List<Student> studentList = studentDao.selectStudents();
studentList.forEach( stu -> System.out.println(stu));
}
6.2实现原理
动态代理
6.3参数理解
6.3.1parameterType
parameterType: 接口中方法参数的类型, 类型的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以推断出具体传入语句的参数,默认值为未设置(unset)。接口中方法的参数从 java 代码传入到mapper 文件的 sql 语句
<delete id="deleteStudent" parameterType="int">
<!-- 这里的parameterType是传入参数的类型-->
delete from student where id=#{studentId}
</delete>
6.3.2传递多个参数@Param
当 Dao 接口方法多个参数,需要通过名称使用参数。在方法形参前面加入@Param(“自定义参数名”),mapper 文件使用#{自定义参数名}。
List<Student> selectStudent( @Param(“personName”) String name ) { … }
mapper 文件: select * from student where name = #{ personName}
接口方法:
List<Student> selectMultiParam(@Param("personName") String name,@Param("personAge") int age);
mapper 文件:
<select id="selectMultiParam" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{personName} or age
=#{personAge}
</select>
6.3.3多个参数-按位置传参
参数位置从 0 开始, 引用参数语法 #{ arg 位置 } , 第一个参数是#{arg0}, 第二个是#{arg1}
注意:mybatis-3.3 版本和之前的版本使用#{0},#{1}方式, 从 mybatis3.4 开始使用#{arg0}方式。
接口方法:
List<Student> selectByNameAndAge(String name,int age);
mapper 文件:
<select id="selectByNameAndAge" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{arg0} or age =#{arg1}
</select>
6.3.4多个参数-使用map
Map 集合可以存储多个值,使用Map向 mapper 文件一次传入多个参数。Map 集合使用 String的 key,
Object 类型的值存储参数。 mapper 文件使用 # { key } 引用参数值。
例如:Map<String,Object> data = new HashMap<String,Object>();
data.put(“myname”,”李力”);
data.put(“myage”,20);
接口方法:
List<Student> selectMultiMap(Map<String,Object> map)
mapper 文件:
<select id="selectMultiMap" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{myname} or age =#{myage}
</select>
6.4封装mybatis输出结果
6.4.1resultType
resultType: 执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名或别名。 注意如果返回的是集
合,那应该设置为集合包含的类型,而不是集合本身。resultType 和 resultMap,不能同时使用
6.4.2resultMap
resultMap 可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给指定属性。
常用在列名和 java 对象属性名不一样的情况。
使用方式:
1.先定义 resultMap,指定列名和属性的对应关系。
2.在中把 resultType 替换为 resultMap。
接口方法:
List<Student> selectUseResultMap(QueryParam param)
实体类中的属性和表格不一致的情况下做一个映射
mapper 文件:
<!-- 创建 resultMap
id:自定义的唯一名称,在<select>使用
type:期望转为的 java 对象的全限定名称或别名
-->
<resultMap id="studentMap" type="com.bjpowernode.domain.Student">
<!-- 主键字段使用 id -->
<id column="id" property="id" />
<!--非主键字段使用 result-->
<result column="name" property="name"/>
<result column="email" property="email" />
<result column="age" property="age" />
</resultMap>
<!--resultMap: resultMap 标签中的 id 属性值-->
<select id="selectUseResultMap" resultMap="studentMap">
select id,name,email,age from student where name=#{queryName} or
age=#{queryAge}
</select>
6.4.3实体类名和列名不同的处理方式
- 使用列别名或者resultType
步骤:- 创建新的实体类 PrimaryStudent
package com.bjpowernode.domain;
/**
* <p>Description: 实体类 </p>
*/
public class PrimaryStudent {
private Integer stuId;
private String stuName;
private Integer stuAge;
// set , get 方法
}
2. 接口方法
List<PrimaryStudent> selectUseFieldAlias(QueryParam param)
3. mapper 文件:
<select id="selectUseFieldAlias"
resultType="com.bjpowernode.domain.PrimaryStudent">
select id as stuId, name as stuName,age as stuAge
from student where name=#{queryName} or age=#{queryAge}
</select
4.测试方法
@Test
public void testSelectUseFieldAlias(){
QueryParam param = new QueryParam();
param.setQueryName("李力");
param.setQueryAge(20);
List<PrimaryStudent> stuList;
stuList = studentDao.selectUseFieldAlias(param);
stuList.forEach( stu -> System.out.println(stu));
}
- 使用resultMap
2. mapper 文件:
<!-- 创建 resultMap
id:自定义的唯一名称,在<select>使用
type:期望转为的 java 对象的全限定名称或别名
-->
<resultMap id="primaryStudentMap"
type="com.bjpowernode.domain.PrimaryStudent">
<!-- 主键字段使用 id -->
<id column="id" property="stuId" />
<!--非主键字段使用 result-->
<result column="name" property="stuName"/>
<result column="age" property="stuAge" />
</resultMap>
<!--resultMap: resultMap 标签中的 id 属性值-->
<select id="selectUseDiffResultMap" resultMap="primaryStudentMap">
select id,name,email,age from student
where name=#{queryName} or age=#{queryAge}
</select>