MyBatis 一文基础总结
老师的随堂上课笔记, 供参考~
1.框架概述
1.1 软件开发常用结构
1.1.1三层架构
三层的处理请求的交互:
用户---> 界面层--->业务逻辑层--->数据访问层--->DB 数据库
1.1.2 常用框架
常见的 J2EE 中开发框架:
MyBatis 框架
MyBatis 是一个优秀的基于 java 的持久层框架,内部封装了 jdbc,开发者只需要关注 sql 语句本身,而不需要处理加载驱动、创建连接、创建 statement、关闭连接,资源等繁杂的过程。MyBatis 通过 xml 或注解两种方式将要执行的各种 sql 语句配置起来,并通过 java 对象和 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。
Spring 框架:
Spring 框架为了解决软件开发的复杂性而创建的。Spring 使用的是基本的 JavaBean 来完成以前,非常复杂的企业级开发。Spring 解决了业务对象,功能模块之间的耦合,不仅在 javase,web 中使用,大部分 Java 应用都可以从 Spring 中受益。Spring 是一个轻量级控制反转(IoC)和面向切面(AOP)的容器。
SpringMVC 框架:
Spring MVC 属于 SpringFrameWork 3.0 版本加入的一个模块,为 Spring 框架提供了构建 Web 应用程序的能力。现在可以 Spring 框架提供的 SpringMVC 模块实现 web 应用开发,在 web 项目中可以无缝使用 Spring 和 Spring MVC 框架。
1.2 框架
1.2.1 框架定义
框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种认为,框架是可被应用开发者定制的应用骨架、模板。在框架基础上加入你要完成的功能。框架安全的,可复用的,不断升级的软件。
2.MyBatis框架入门
2.1MyBatis能做什么
一个框架,早期叫做ibatis, 代码在github
mybatis是 MyBatis SQL Mapper Framework for Java (sql映射框架)
1)sql mapper :sql映射 可以把数据库表中的一行数据 映射为 一个java对象。 一行数据可以看做是一个java对象。操作这个对象,就相当于操作表中的数据
2) Data Access Objects(DAOs) : 数据访问 , 对数据库执行增删改查。
mybatis提供了哪些功能:
提供了创建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对象(表中的数据)
总结: mybatis是一个sql映射框架,提供的数据库的操作能力。增强的JDBC, 使用mybatis让开发人员集中精神写sql就可以了,不必关心Connection,Statement,ResultSet 的创建,销毁,sql的执行。
2.2MyBaits的使用步骤
2.2.1 搭建MyBatis开发环境
1.下载MyBatis
https://github.com/mybatis/mybatis-3/releases
2.把MyBatis的jar包放入maven的本地仓库
2.2.2Mybatis的使用步骤
1.新建的student表
2.加入maven的mybatis坐标, mysql驱动的坐标
<!-- 加入mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- mysql的依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
3.创建实体类,student--保存表中的一行数据的
4.创建持久层的dao接口,定义操作数据库的方法
5.创建一个mybatis使用的配置文件叫做sql映射文件:写sql语句的。一般一个表一个sql映射文件。这个文件是xml文件。 6.创建mybatis的主配置文件:一个项目就一个主配置文件。主配置文件提供了数据库的连接信息和sql映射文件的位置信息
7.创建使用mybatis类,通过mybatis访问数据库
1、创建maven工程
2、创建Student表
3、创建Student类
4、创建接口StudentDao
5、创建StudentDao接口的映射文件(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">
<!--一般使用接口的全限定类名-->
<mapper namespace="net.zhenghou.dao.StudentDao">
<!--
select表示查询
id:你要执行的sql语法的唯一标识,mybatis会使用这个值来找到要执行的的sql语句
可以自定义,但是要求你使用接口中方法名称
-->
<select id="selectStudents" resultType="net.zhenghou.domain.Student">
select * from student
</select>
<!-- #{属性名},这个必须和类的属性一致-->
<insert id="insertStudent">
insert into student values(#{id},#{name},#{birthday},#{address})
</insert>
<update id="updateStudent">
update student set name=#{name},birthday=#{birthday},address=#{address} where id=#{id}
</update>
<delete id="deleteStudent">
delete from student where id=#{id}
</delete>
</mapper>
<!--
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
1. 指定约束文件
2.约束文件的作用:
检查当前文件中出现的标签,属性必须符合mybatis的要求
3.mapper是当前文件的根标签,必须的
namespace:叫做命名空间,唯一值,,可以自定义一个字符串,
一般我们要求是接口的全限定类名
4. 在当前文件中,可以使用特定的标签,标识数据库的特定操作
select的标签 :表示的是查询,selec语句
update标签:表示更新数据库的操作
insert标签:表示插入
delete标签,表示删除
-->
6.创建主配置文件
7.创建sqlSession执行sql语句
2.2.3 插入操作
1. StudentDao 的接口中加入一个抽象方法
2. 在StudentDao对应的映射文件StudentDao.xml中 插入操作
2.2.4 配置日志功能
2.2.5修改操作
2.2.6删除操作
2.3MyBatis对象分析
2.3.1Resources类
Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。
2.3.2 SqlSessionFactoryBuilder 类
SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。 由 于
SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。所以,一般会将
该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。
2.3.3 SqlSessionFactory 接口
SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用
只需要一个该对象即可。创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。
➢ openSession(true):创建一个有自动提交功能的 SqlSession
➢ openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交
➢ openSession():同 openSession(false)
2.3.4 SqlSession接口
SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以
SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。
SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将
其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭。
2.3.5 创建工具类
public class MyBatisUtils {
private static SqlSessionFactory factory;
static{
try {
String config="mybatis.xml";
InputStream is = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取sqlSession的方法
public static SqlSession getSqlSession(){
SqlSession sqlSession=null;
if(factory !=null){
sqlSession= factory.openSession();//非自动提交
}
return sqlSession;
}
}
3 MyBatis使用Dao 开发方式
3.1Dao开发
3.1.1传统Dao开发方式的分析
SqlSession的API方法定位到映射文件mapper中相应的sql语句,从而对数据库进行操作。
所以把这种由动态代理方式实现。
Mapper动态代理方式无需程序员实现Dao接口,接口是由MyBatis结合映射文件自动生成的动态代理实现的。
3.2 MyBatis框架Dao代理
3.2.1Dao代理实现CRUD
步骤:
去掉Dao接口的实现类
getMapper获取代理对象
接口 接口对象=sqlSession.getMapper(接口的class对象)
3.2.2原理
org.apache.ibatis.binding.MapperProxy@4d95d2a2
MapperProxy类的定义:
public class MapperProxy<T> implements InvocationHandler, Serializable {
......
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
....
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
重点的方法:
mapperMethod.execute(this.sqlSession, args);
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
3.3 深入理解参数
1.parameterType
接口中方法参数的类型,类型是全限定类名。它是可选的。
因为mybatis可以推断出具体传入语句的参数,默认值为unset(不设置)
<delete id="deleteStudent" parameterType="java.lang.Integer">
delete from student where id=#{id}
</delete>
2.MyBatis传递参数
从java代码中把参数传递到mapper.xml文件
一个简单参数:
Dao接口中方法的参数只有一个简单类型(java基本类型+String),占位符#{任意字符},和方法的参数名无关
接口中的方法:
int deleteStudent(int id);
mapper文件
<delete id="deleteStudent" parameterType="java.lang.Integer">
delete from student where id=#{studentId}
</delete>
#{studentId}:studentId是自定义的变量名称,和方法参数名无关
多个参数--使用@param(重点)
当dao接口方法有多个参数,需要通过名称是使用参数。在方法的形参前面加入
@Param("自定义参数名")
在mapper文件使用#{自定义参数名}
接口
List<Student> selectMultiParam(@Param("studentName") String name, @Param("studentAddress") String address );
mapper文件
<select id="selectMultiParam" resultType="net.zhenghou.domain.Student">
select * from student where name=#{studentName} or address=#{studentAddress}
</select>
测试
@Test
public void testSelectMultiParam(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = studentDao.selectMultiParam("丁凯", "南京市");
studentList.forEach(student-> System.out.println(student));
}
多个参数--使用对象(重点)
使用java对象传递参数,java的属性值就是sql需要的参数值。每一个属性就是一个参数。
#{property}
创建 QueryParam对象
public class QueryParam {
private String queryName;
private String queryAddress;
public QueryParam() {
}
public String getQueryName() {
return queryName;
}
public void setQueryName(String queryName) {
this.queryName = queryName;
}
public String getQueryAddress() {
return queryAddress;
}
public void setQueryAddress(String queryAddress) {
this.queryAddress = queryAddress;
}
}
接口
List<Student> selectMultiObject(QueryParam queryParam);
mapper文件
<select id="selectMultiObject" resultType="net.zhenghou.domain.Student">
select * from student where name=#{queryName} or address=#{queryAddress}
</select>
测试
@Test
public void testSelectMultiObject(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
QueryParam qp=new QueryParam();
qp.setQueryAddress("南京市");
qp.setQueryName("毕浪");
studentDao.selectMultiObject(qp);
}
多个参数--按位置(了解)
参数位置从0开始,引用参数语法#{arg位置}。第一个参数是#{arg0 } 第二个参数是#{arg1}
接口方法
List<Student> selectByNameAndAddess(String name,String address);
mapper文件
<select id="selectByNameAndAddess" resultType="net.zhenghou.domain.Student">
select * from student where name=#{arg0} or address=#{arg1}
</select>
测试方法:
@Test
public void testSelectByNameAndAddress(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = studentDao.selectByNameAndAddess("毕浪", "南京市");
studentList.forEach(student -> System.out.println(studentList));
}
多个参数-使用Map(了解)
Map集合可以存储多个值,使用Map向mapper文件一次传入多个参数。Map集合使用String的key,Object类型的值存储参数
#{key} 来引用
接口方法
List<Student> selectMultiMap(Map<String,Object> map);
mapper文件
<select id="selectMultiMap" resultType="net.zhenghou.domain.Student">
select * from student where name=#{myname} or address=#{myaddress}
</select>
测试方法
@Test
public void testSelectMap(){
Map<String,Object> map=new HashMap<String,Object>();
map.put("myname","唐雨鑫");
map.put("myaddress","无锡市");
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = studentDao.selectMultiMap(map);
studentList.forEach(student -> System.out.println(studentList));
}
3.4#和$ 的区别
#:占位符。告诉mybatis使用实际的参数值代替。并使用PreparedStatement对象执行sql语句,#{...}代替了sql语句中?。这样做更安全,也是首选做法。
:字符串的替换。告诉mybatis使用包含的“字符串"替换所在位置。使用Statement把sql语句和${}的内容连接起来。主要用在替换表名,列名,不同列排序等操作。
3.5 封装mybatis输出结果
1.resultType
执行sql得到的ResultSet转换的类型
a. 简单类型
接口
int countStudent();
mapper文件
<!-- resultType="java.lang.Integer"-->
<select id="countStudent" resultType="int">
select count(*) from student
</select>
测试
@Test
public void testReturnInt(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
int count = studentDao.countStudent();
System.out.println("学生的总数是:"+count);
}
b.对象类型
mybatis框架的处理:使用构造方法创建对象。调用setXXX给属性赋值
Class.forName(全限定类名): 类的Class对象
类名 对象= class对象.newInstance()
Student对象
sql语句列 java对象方法 调用对应set方法
id setId(rs.getInt("id")) id列--->setId()
name setName(rs.getString("name")) name--->setName()
birthday setBirthday(rs.getString("birthday")) birthday-->setBirthday()
address setAddress(rs.getString("address")) address-->setAddress()
注意点:
dao接口方法返回的是集合类型,需要指定集合中的类型,而不是集合本身的类型。
c.Map
sql的查询结果作为Map的key和value,推荐使用Map<Object,Object>
注意点: Map作为接口返回值,sql语句的查询结果最多只能有一条记录,大于一条记录就会报错
接口
Map<Object,Object> selectReturnMap(int id);
mapper文件
<select id="selectReturnMap" resultType="java.util.HashMap">
select name,birthday,address from student where id=#{studentId}
</select>
测试
@Test
public void testReturnMap() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Map<Object, Object> returnMap = studentDao.selectReturnMap(4);
System.out.println("查询的结果是:"+returnMap);
}
2.resultMap
可以自定义sql的结果和java对象的映射关系,更灵活的把列值赋值给指定的属性
通常用于列名和java对象属性名不一样的情况。
使用方式:
1.先定义resultMap,指定列名和属性的对应关系
2.在select标签中把resultType替换为resultMap
接口
List<Student> selectUseResultMap(QueryParam queryParam);
mapper文件
<!-- 创建resultMap
id:自定义的唯一名称,在select使用
type:期望转换为java对象的全限定类名或者别名
主键字段使用id column:列名property:属性名
-->
<resultMap id="studentMap" type="net.zhenghou.domain.Student">
<!-- 主键字段使用id -->
<id column="id" property="id"/>
<!-- 非主键字段使用result-->
<result column="name" property="name"/>
<result column="address" property="address" />
<result column="birthday" property="birthday" />
</resultMap>
<!-- resultMap: resultMap标签中的id属性值 -->
<select id="selectUseResultMap" resultMap="studentMap">
select id,name,birthday,address from student where name= #{queryName} or address=#{queryAddress}
</select>
测试
@Test
public void testSelectUseResultMap(){
QueryParam qp=new QueryParam();
qp.setQueryName("李四");
qp.setQueryAddress("无锡市");
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = studentDao.selectUseResultMap(qp);
studentList.forEach(student -> System.out.println(student));
}
3. 实体类属性名和列名不同的处理
1.给列名取别名和resultType
接口
List<PrimaryStudent> selectUseFieldAlias(QueryParam queryParam);
mapper文件
<select id="selectUseFieldAlias" resultType="net.zhenghou.domain.PrimaryStudent">
select id as stuId,name stuName,birthday stuBirthday,address stuAddress from student where name= #{queryName} or address=#{queryAddress}
</select>
测试
@Test
public void testSelectUseFieldAlias(){
QueryParam qp=new QueryParam();
qp.setQueryName("李四");
qp.setQueryAddress("无锡市");
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<PrimaryStudent> primaryStudents = studentDao.selectUseFieldAlias(qp);
primaryStudents.forEach(stu-> System.out.println(stu));
}
2.使用resultMap
接口方法
List<PrimaryStudent> selectUsedDiffResultMap(QueryParam queryParam);
mapper文件
<resultMap id="primaryStudentMap" type="net.zhenghou.domain.PrimaryStudent">
<!-- 主键字段 id-->
<id column="id" property="stuId"/>
<!-- 非主键字段 result-->
<result column="name" property="stuName"/>
<result column="address" property="stuAddress"/>
<result column="birthday" property="stuBirthday"/>
</resultMap>
<select id="selectUsedDiffResultMap" resultMap="primaryStudentMap">
select id,name,birthday,address from student where name= #{queryName} or address=#{queryAddress}
</select>
测试方法
@Test
public void testSelectDiffResultMap(){
QueryParam qp=new QueryParam();
qp.setQueryName("李四");
qp.setQueryAddress("无锡市");
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<PrimaryStudent> studentList = studentDao.selectUsedDiffResultMap(qp);
studentList.forEach(stu-> System.out.println(stu));
}
3.6模糊查询 like
模糊查询的实现方式一种有两种:
1.java代码中给查询的数据加上”%“
2.在mapper文件的sql语句的条件中加上“%”
接口方法
List<Student> selectLikeFirst(String name);
List<Student> selectLikeSecond(String name);
mapper文件
<select id="selectLikeFirst" resultType="net.zhenghou.domain.Student">
select id,name,birthday,address from student where name like #{studentName}
</select>
<!-- like name #{}"%"-->
<select id="selectLikeSecond" resultType="net.zhenghou.domain.Student">
select id,name,birthday,address from student where name like #{name} "%"
</select>
测试方法
@Test
public void testSelectLikeJava(){
String name="丁%";
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = studentDao.selectLikeFirst(name);
studentList.forEach(stu-> System.out.println(stu));
}
@Test
public void testSelectLikeMapper(){
String name="丁";
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = studentDao.selectLikeSecond(name);
studentList.forEach(stu-> System.out.println(stu));
}
4.MyBatis--动态SQL
动态SQL。通过mybatis提供的各种标签对条件判断以实现动态拼接sql语句。
常用的动态标签:
<if/> <where> < choose/> <foreach>
在mapper的动态SQL中如果出现大于号,小于号,大于等于号,小于等于号 等,最好将其转换为实体符号,否则xml可能会出现解析出错问题
特别是对于小于号(<),在xml中是绝对不能出现的。否则解析mapper文件会出错
符号 含义 实体符号
> 大于 >;
< 小于 < ;
>= 大于等于 > ;=
<= 小于等于 < ;=
4.1动态SQL if标签
语法
<if test="条件">sql语句的部分</if>//当test的值为true,会将包含的sql的片段拼接到其所在的SQL语句中
接口方法
public interface StudentDao {
List<Student> selectStudentIf(Student student);
}
mapper文件
<select id="selectStudentIf" resultType="net.zhenghou.domain.Student">
select id,name,birthday,address from student where 1=1
<if test=" name !=null and name !=''">
and name=#{name}
</if>
<if test="id>10">
and id >#{id}
</if>
</select>
测试
@Test
public void testSelectIf(){
Student student=new Student();
student.setName("");
student.setId(87);
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = studentDao.selectStudentIf(student);
studentList.forEach(stu-> System.out.println(stu));
}
4.2动态SQL where标签
语法
<where> 其它动态sqln </where> //如果有查询条件,会自动添加where语句;没有查询时候不会添加where子句
4.3 动态SQL foreach标签
foreach标签用于实现对于数组与集合的遍历。对其使用,需要注意
collection 表示要遍历的集合类型 list array等
open close separator 为对遍历内容的SQL拼接
语法:
<foreach collection="集合类型" open=”开始的字符" close="结束的字符" item=“集合中的成员" separator="集合成员之间的分隔符">
</foreach>
4.4 动态SQL--代码片段
sql标签用于定义SQL的片段,以便其它SQL标签复用。如果其它的标签需要用到该SQL的片段
<sql> </sql> 复用的sql
<include/> //引用sql的片段
<sql id="studentSql">
select id,name,birthday,address from student
</sql>
<select id="selectStudentSqlFragment" resultType="net.zhenghou.domain.Student">
-- 引用sql片段
<include refid="studentSql"/>
<if test="list !=null and list.size()>0">
where id in
-- collectionL类型 list array open:开始的字符 close 结束的字符
<foreach collection="list" open="(" close=")" item="stuObject" separator=",">
#{stuObject.id}
</foreach>
</if>
</select>
总结:
可以将一些重复性的SQL语句进行抽取,以达到复用的效果
小结:
动态sql主要对映射文件配置:
<select>:查询
id:和接口中的方法保持一致
resultType:
resultMap:
<insert>插入
id:和接口中的方法保持一致
<update> 修改
id:和接口中的方法保持一致
<delete>删除
<where>:where条件
<if>:判断
<foreach>: 循环
<sql> :sql片段的抽取
id;
<include>
refid=“sql的id保持一致"
5.MyBatis的配置文件
5.1 主配置文件
5.5.1特点:
1.xml文件,需要在头部使用约束文件
<?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">
2.根标签
<configuration>
3.主要包含内容
定义别名
数据源
mapper文件
5.5.2 dataSource标签
<dataSource type="POOLED">
1.连接的类型
POOLED:使用连接池的数据源
UNPOOLED:不使用连接池的数据源
JNDI:采用JNDI实现的数据源
2.dataSource的配置
<dataSource type="POOLED">
<!-- 连接数据库的四个要素 -->
<!-- 连接数据库驱动的名 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- 连接数据库的url的字符串 -->
<property name="url" value="jdbc:mysql://localhost:3306/chenxp"/>
<!-- 访问数据库的用户名-->
<property name="username" value="root"/>
<!-- 访问数据库的密码-->
<property name="password" value="root"/>
</dataSource>
5.5.3事务
<transactionManager type="JDBC"/> //指定mybatis使用的事务管理器,mybatis支持两种事务类型JDBC和MANAGED
MANAGED:由容器来管理事务的整个生命周期(Spring 容器)
JDBC默认是手动提交,如需自动提交 openSession(true)
5.5.4 使用数据库属性配置文件中读取这些数据
mybatis主配置文件需要从这个属性配置文件
步骤:
1.在resurces目录创建jdbc.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/chenxp
jdbc.username=root
jdbc.password=root
2.在主配置文件,文件configuration开始位置加入
<properties resource="jdbc.properties"/>
3.使用key指定值
<dataSource type="POOLED">
<!-- 连接数据库驱动的名 -->
<property name="driver" value="${jdbc.driver}"/>
<!-- 连接数据库的url的字符串 -->
<property name="url" value="${jdbc.url}"/>
<!-- 访问数据库的用户名-->
<property name="username" value="${jdbc.username"/>
<!-- 访问数据库的密码-->
<property name="username" value="${jdbc.password}"/>
</dataSource>
5.5.5 typeAliases(类型别名)
mybatis支持默认别名,但我们也可以采用自定义别名方式来开发,主要使用在<select resultType="别名">
定义settings标签的后面
<typeAliases>
<typeAliase type=”“ alias=” "/>
</typeAliases>
6.分页插件--PageHelper
Mybatis分页插件:
1.在企业级开发中,目前使用mybatis是不带分页的。如果想实现分页,我们可以手动编写limit语句来实现分页(mysql).不同的数据库实现分页的sql语句有所不同,所以手写分页成本脚本。所以此时可以借助分页插件来实现分页功能
2.PageHelper插件:第三方分页插件。将复杂的分页操作进行封装,从而让分页功能变得非常简单
pageHelper的使用:
1.jar包拷贝上本地仓库
2.pom.xml中加入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
3 .在mybatis.xml中
<!-- 指定插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>
4.测试
@Test
public void testPageHelper(){
//设置分页参数
PageHelper.startPage(3,2);
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> studentList = studentDao.selectStudents();
studentList.forEach(stu-> System.out.println(stu));
PageInfo<Student> pageInfo=new PageInfo<Student>(studentList);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示的长度:"+pageInfo.getPageSize());
System.out.println("是否第一页"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
}
分页插件总结:
' 分页助手相关的API
PageHelper;分页助手功能类
PageInfo:分页相关参数功能类
new PageInfo(输入当页的数据list)
页助手相关的API
PageHelper.startPage() 设置分页参数
PageInfo对象.getTotal() 获取总条数
PageInfo对象.getPageSize() 获取每页显示的条数
PageInfo对象.getPrePage() 获取上一页
PageInfo对象.getNextPage() 获取下一页
PageInfo对象.isIsFirstPage() 获取是否是第一页
PageInfo对象.isIslastPage() 获取是否是最后一页
7.MyBatis的多表操作
7.1多表的模型
一对一:
在任意一方建立外键,关联对方的主键
一对多:
在任意一方建立外键,关联对方的主键
多对多:借助中间表,中间表至少两个字段,分别关联的两张表的主键
7.2一对一的模型:
1.人和身份证:一个人只有一个身份证。
2.代码实现
步骤一:数据库sql语句的准备
create table person(
id int primary key auto_increment,
name varchar(20),
age int);
insert into person values(null,'汤雨轩',22);
insert into person values(null,'周雨童',22);
insert into person values(null,'李阳',22);
create table card(
id int primary key auto_increment,
number varchar(30),
pid int,
CONSTRAINT person_card_pid FOREIGN KEY (pid) REFERENCES person(id));
insert into card values(null,'1233',1);
insert into card values(null,'5633',2);
insert into card values(null,'4356',3);
步骤二:实体类
public class Person {
private Integer id;//主键id
private String name;//人的姓名
private Integer age;//人的年龄
public class Card {
private Integer id;
private String number;//身份证号
private Person p;//所属人的对象
...........
}
步骤三:
接口方法
public interface PersonCardDao {
List<Card> queryAll();
}
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">
<!--一般使用接口的全限定类名-->
<mapper namespace="net.zhenghou.dao.PersonCardDao">
<!-- 配置字段和实体对象属性的映射关系-->
<resultMap id="oneToOne" type="card">
<!-- 主键 id-->
<id column="cid" property="id"/>
<!-- 非主键 reuslt-->
<result column="number" property="number" />
<!-- association 配置被包含对象的映射关系-->
<association property="p" javaType="person">
<id column="pid" property="id"/>
<result column="name" property="name" />
<result column="age" property="age" />
</association>
</resultMap>
<select id="queryAll" resultMap="oneToOne">
SELECT c.id cid,c.number,c.pid,p.id,p.name,p.age
from card c,person p
where c.pid=p.id
</select>
</mapper>
步骤四:主配置文件中加入mapper的路径
<mapper resource="net/zhenghou/dao/PersonCardDao.xml"/>
步骤五: 测试
@Test
public void testOneToOne(){
SqlSession sqlSession= MyBatisUtils.getSqlSession();
PersonCardDao personCardDao = sqlSession.getMapper(PersonCardDao.class);
List<Card> cards = personCardDao.queryAll();
cards.forEach(card-> System.out.println(card));
}
练习题:
查询一个订单,同时查询出该订单所属的用户(一个订单只属于一个用户)
订单order(id,ordertime,total,uid)
用户user(id,username,password,birthday)
create table user(id int primary key auto_increment,
username varchar(30),
password varchar(30),
birthday varchar(20));
create table orders(id int primary key auto_increment,
ordertime varchar(255),
total double,
uid int,
CONSTRAINT user_orders_id FOREIGN KEY(uid) REFERENCES user(id));
insert into user values(null,'闫云光','123','2000-12-12');
insert into user values(null,'谢志青','123','1999-12-12');
insert into user values(null,'李明耀','123','1998-12-12');
insert into orders values(null,'2022-5-15',2000,1);
insert into orders values(null,'2022-5-15',3000,2);
insert into orders values(null,'2022-5-15',2000,3);
7.3一对多的模型:
1.班级和学生,一个班级有多个学生
2.代码实现
步骤一:数据库的sql语句
create table classes(
id int primary key auto_increment,
name varchar(20));
create table student(
id int primary key auto_increment,
name varchar(30),
age int,
cid int,
CONSTRAINT class_student_id FOREIGN KEY(cid) REFERENCES classes(id));
insert into classes values(null,'开发08');
insert into classes values(null,'开发09');
insert into student values(null,'喻宏波',22,1);
insert into student values(null,'毕浪',22,1);
insert into student values(null,'张三',22,2);
insert into student values(null,'李四',22,2);
步骤二:实体类
public class Student {
private Integer id;
private String name;
private Integer age;
public Student() {
}
........
}
public class Classes {
// 主键id
private Integer id;
// 班级名称
private String name;
// 班级中所有学生对象
private List<Student> students;
public Classes() {
}
步骤三:接口方法
public interface ClassStudent {
List<Classes> queryAll();
}
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">
<!--一般使用接口的全限定类名-->
<mapper namespace="net.zhenghou.dao.ClassStudent">
<resultMap id="oneToMany" type="classes">
<id column="cid" property="id" />
<result column="cname" property="name" />
<!--
collection:配置包含的集合对象的映射关系
property: 被包含对象的变量明
ofType: 被包含对象的实际数据类型
-->
<collection property="students" ofType="student">
<id column="sid" property="id" />
<result column="sname" property="name"/>
<result column="sage" property="age"/>
</collection>
</resultMap>
<select id="queryAll" resultMap="oneToMany">
select c.id cid,c.name cname,s.id sid,s.name sname,s.age sage
from classes c,student s
where c.id=s.cid
</select>
</mapper>
步骤四:测试
@Test
public void testOneToMany(){
SqlSession sqlSession= MyBatisUtils.getSqlSession();
ClassStudent classStudentDao = sqlSession.getMapper(ClassStudent.class);
List<Classes> classes = classStudentDao.queryAll();
classes.forEach(c-> System.out.println(c));
}
练习题:
用户表和订单表的关系:一个用户有多个订单
7.4 多对多的关系
1.学生和课程 :一个学生可以选择多门课程,一个课程也可以被多个学生所选择
2.代码实现
步骤一:数据库sql语句
create table course(
id int primary key auto_increment,
name varchar(20)
);
create table student_course(
id int primary key auto_increment,
sid int,
cid int,
CONSTRAINT sc_student_fk FOREIGN KEY(sid) REFERENCES student(id),
CONSTRAINT sc_course_fk FOREIGN KEY(cid) REFERENCES classes(id)
);
insert into course values(null,"java基础");
insert into course values(null,"javaweb");
insert into student_course values(null,1,1);
insert into student_course values(null,1,2);
insert into student_course values(null,2,1);
insert into student_course values(null,2,2);
步骤二:实体类
需改学生表
public class Student {
private Integer id;
private String name;
private Integer age;
private List<Course> course;//学生所轩的课程集合
...
}
public class Course {
private Integer id;//主键id
private String name;//课程名
....
}
步骤三:
接口方法
public interface CourseStudent {
List<Student> queryAll();
}
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">
<!--一般使用接口的全限定类名-->
<mapper namespace="net.zhenghou.dao.CourseStudent">
<resultMap id="manyToMany" type="student">
<id column="id" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age" />
<collection property="course" ofType="course">
<id column="cid" property="id"/>
<result column="cname" property="name" />
</collection>
</resultMap>
<select id="queryAll" resultMap="manyToMany">
</select>
</mapper>
步骤四:
主配置文件
<mapper resource="net/zhenghou/dao/CourseStudent.xml"/>
步骤五:测试
@Test
public void manyToMany(){
SqlSession sqlSession= MyBatisUtils.getSqlSession();
CourseStudent courseStudentDao = sqlSession.getMapper(CourseStudent.class);
List<Student> students = courseStudentDao.queryAll();
students.forEach(c-> System.out.println(c));
}
练习题:
用户表和角色表的关系:一个用户有多个角色,一个角色被多个用户使用
user_role(id,user_id,role_id);
role(id,rolename)
7.5多表模型的总结
<resultMap id="resultMap的id">
//主键
<id column="数据库中表的列名" property="实体类中的属性名">
//非主键
<result column="数据库中表的列名" property="实体类中的属性名">
//一对一的关系
<association property="被包含的对象的变量名" javaType="被包含对象的数据类型" >
<id column="数据库中表的列名" property="实体类中的属性名">
//非主键
<result column="数据库中表的列名" property="实体类中的属性名">
</association>
//一对多,多对多的关系
<collection propety="被包含集合对象的变量名" ofType="集合中保存对象的数据类型">
<id column="数据库中表的列名" property="实体类中的属性名">
//非主键
<result column="数据库中表的列名" property="实体类中的属性名">
</collection>
</resultMap>
<select id="" resultMap="resultMap的id">
</select>
8.MyBatis注解开发-单表操作
使用注解开发,可以减少Mapper映射文件。
8.1Mybatis常用的注解
注解 含义
@Select 实现查询
@Insert 实现新增
@Delete 实现删除
@Update 实现修改
@Reuslt 实现结果集封装
@Reuslts 可以与result一起使用,封装多个结果集
@One 实现一对一结果集封装
@Many 实现一对多结果集封装
8.2MyBatis注解的CRUD
步骤一:创建Mapper接口
public interface StudentMapper {
//查询全部
@Select("SELECT * FROM student")
List<Student> queryAll();
// 新增操作
@Insert("INSERT INTO student( name,age,sex)VALUES (#{name},#{age},#{sex})")
Integer insert(Student student);
//修改操作
@Update("UPDATE student SET name=#{name},age=#{age},sex=#{sex} WHERE id=#{id}")
Integer update(Student student);
//删除操作
@Delete("DELETE FROM student WHERE id=#{id}")
Integer delete(Integer id);
}
步骤二:主配置文件mybatis.xml中增加
!-- sql mapper(sql映射文件)的位置-->
<mappers>
<!-- 扫描使用注解类-->
<mapper class="net.zhenghou.mapper.StudentMapper"></mapper>
<!-- 指定扫描使用注解的类所在的包-->
<!-- <package name="net.zhenghou.mapper"/>-->
</mappers>
步骤三:测试类
public class TestStudent {
@Test
public void insert(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student=new Student();
student.setName("tom");
student.setAge(21);
student.setSex("male");
mapper.insert(student);
sqlSession.commit();
sqlSession.close();
}
@Test
public void update() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = new Student();
student.setId(2);
student.setName("jerry");
student.setAge(20);
student.setSex("male");
mapper.update(student);
sqlSession.commit();
sqlSession.close();
}
@Test
public void select() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.queryAll();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
@Test
public void delete() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int id=2;
mapper.delete(2);
sqlSession.commit();
sqlSession.close();
}
}
8.3注解开发总结
注解可以简化开发操作,省略映射文件的编写
常用注解:
@Select ("查询的sql语句")
@Insert ("新增的sql语句")
@Update ("修改的sql语句")
@Delete("删除的sql语句")
配置映射关系:
<!-- 扫描使用注解类-->
<mapper class="net.zhenghou.mapper.StudentMapper"></mapper>
//或者
<!-- 指定扫描使用注解的类所在的包-->
<package name="net.zhenghou.mapper"/>
9.MyBatis注解开发-多表操作
9.1MyBatis注解实现复杂映射开发
使用@Results @Reuslt @One @Many注解组合完成复杂关系的配置
注解 说明
@Results 代替的是标签<resultMap>,这个注解中也可以使用单个@Result注解,也可以使用多个@Result集合,使用格式:
@Results({@Result(),@Result(),.......})或者@Results(@Result())
@Result 代替<id>标签和<result>标签
@Result中属性介绍:
column :数据库的列名
property:属性名
one:需要使用@One注解(@Result(one=@One)()))
many: 需要使用@Many注解(@Result(many=@Many)())
注解 含义
@One(一对一) 代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询的返回单一对象。
@One注解属性介绍:
select:指定用来多表查询的sqlmapper
使用格式:
@Result(column="",property="",one=@One(select=""))
@Many(一对多) 代替了<collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象的集合。
使用格式:@Result(column="",property="",many=@Many(select=""))
9.2一对一查询
9.2.1步骤
public interface CardMpper {
//查询全部
@Select("select * from card")
@Results(
{
@Result(column="id",property = "id"),
@Result(column="number",property = "number"),
@Result(
property = "p",//被包含对象的变量名
javaType = Person.class,//被包含对象的实际数据类型
column="pid",//根据查询出的card表中pid字段来查询
//one=@One() 一对一固定写法
//select属性,指定调用哪个接口中的哪个方法
one = @One(select="net.zhenghou.mapper.PersonMapper.selectById")
)
}
)
List<Card> selectAll();
}
9.3一对多查询
9.3.1步骤
1.查询一个班级,同时查询出该班级对应的学生信息
2.对应的查询语句
select * from classes
select * from student where cid=#{id}
3.创建接口
public interface Student1Mapper {
//根据cid班级号查询student表
@Select("select * from student where cid=#{cid}")
List<Student> selectByCid(Integer cid);
}
public interface ClassMapper {
//查询所有的班级信息
@Select("select * from classes")
@Results(
{
@Result(
property = "students",//被包含的对象的变量名
javaType = List.class,//被包含对象的实际数据类型
column="id",//根据查询的classes表中的id字段来查询
//student many=@Many一对多固定的写法
// select属性表示指定调用哪个接口中的哪个方法
many = @Many(select = "net.zhenghou.mapper.Student1Mapper.selectByCid")
)
}
)
List<Classes> selectAll();
}
4.测试
@Test
public void selectAllClasses(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
ClassMapper mapper = sqlSession.getMapper(ClassMapper.class);
List<Classes> classes = mapper.selectAll();
classes.forEach(c-> System.out.println(c));
sqlSession.close();
}
9.4多对多查询
9.4.1步骤
1.查询学生以及所对应的课程信息
2.对应的sql语句
select distinct s.id,s.name,s.age
from student s,student_course sc
where s.id=sc.sid;
select c.id,c.name
from course c,student_course sc
where c.id=sc.id and sc.sid=#{sid}
3.mapper接口
public interface CourseMapper {
//根据学生的id,查询所选的课程
@Select("select c.id,c.name from course c,student_course sc where c.id=sc.id and sc.sid=#{id}")
List<Course> selectById(Integer id);
}
public interface Student1Mapper {
//根据cid班级号查询student表
@Select("select * from student where cid=#{cid}")
List<Student> selectByCid(Integer cid);
@Select("select distinct s.id,s.name,s.age from student s,student_course sc where s.id=sc.sid")
@Results({
@Result(column = "id",property = "id"),
@Result(column = "name",property = "name"),
@Result(column = "age",property = "age"),
@Result(
property = "course",
javaType = List.class,
column = "id",
many=@Many(select="net.zhenghou.mapper.CourseMapper.selectById")
)
})
List<Student> selectByCourse();
}
4.测试
@Test
public void selectAllStudentCourse(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
Student1Mapper mapper = sqlSession.getMapper(Student1Mapper.class);
List<Student> studentList = mapper.selectByCourse();
studentList.forEach(c-> System.out.println(c));
sqlSession.close();
}
9.4.2多对多总结
参考一:对多总结