MyBits框架

软件开发常用结构

三层架构

三层架构包含的三层:

界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)

三层的职责:

  1. 界面层(表示层,视图层):主要功能是接受用户的数据,显示请求的处理结果。使用 web 页面和 用户交互,手机 app 也就是表示层的,用户在 app 中操作,业务逻辑在服务器端处理。 主要是和用户打交道,接受用户的的请求参数,显示处理结果的。(jsp,html,servlet)
  2. 业务逻辑层:接收界面层传递过来的数据,检查数据,计算业务逻辑,调用数据访问层获取数据。
  3. 数据访问层:与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交 给业务层,同时将业务层处理的数据保存到数据库.

三层对应的包:

界面层:controller包(servlet...)

业务逻辑层:service包

数据访问层:dao包

三层中类的交互流程:

用户---> 界面层--->业务逻辑层--->数据访问层(持久层)--->DB 数据库(mysql)

三层架构对应的框架:

界面层------------>servelt---------->springmvc(框架)

业务逻辑层------>service类------>spring(框架)

数据访问层------->dao类--------->mybatis(框架)

框架

框架定义

框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方 法;另一种认为,框架是可被应用开发者定制的应用骨架、模板。

简单的说,框架其实是半成品软件,就是一组组件,供你使用完成你自己的系统。它定义好了一些基础功能,需要加入自己的功能就是完整的,基础功能是可重复使用的,可升级的。从另一个角度来 说框架一个舞台,你在舞台上做表演。在框架基础上加入你要完成的功能。

框架特点:

1)框架一般不是全能的,不能做所有的事情

2)框架是针对某一个领域有效,特长是在某一个方面,比如mybatis做数据库操作强,但是他不能做其他的

3)框架是一个软件

使用JDBC的缺陷:

1. 代码比较多,开发效率低

2.需要关注 Connection ,Statement, ResultSet 对象创建和销毁

3.对 ResultSet 查询的结果,需要自己封装为 List

4.重复的代码比较多些

5.业务代码和数据库的操作混在一起

MyBatis框架

一个持久层框架,早期叫做ibatis,mybatis是MyBatis SQL Mapper FrameWork for Java(sql映射框架),MyBatis可以操作数据库,对数据进行增删改查,Mybatis可以看做是高级的jdbc,解决jdbc的缺点。

1)sql mapper : sql 映射

可以吧数据库中的一行数据,映射成一个java对象,一行数据可以看做是一个java对象。操作这个对象就相当于操作表中的数据。

2)Data Access Object (DAOs):数据访问

对数据库执行增删改查。

mybatis提供的功能:

0.注册驱动

1.提供了创建Connection,statement,Resultset的能力,不用开发人员创建这个对象了

2.提供了执行sql语句的能力

3.提供了循环sql,把sql结果转换为java对象,List集合的能力。

4.提供了关闭资源的能力,不用自己关闭connection,statement,resultset

开发人员需要做的是:提供sql语句。

流程:开发人员提供sql语句----->mybatis处理sql----->开发人员得到List集合或者java对象(表中的数据)。

总结:

mybatis是一个sql映射框架,提供的数据库的操作能力,增强的jdbc,使用mybatis让开发人员集中精神写sql就行了,不必关心Connection,Statement,ResultSet的创建,销毁,SQL的执行。

MyBatis入门:第一个MyBatis的创建

实现步骤:

1.创建student表(id,name,email,age)

2.新建maven项目

3.修改pom.xml文件

1)加入依赖:MyBatis依赖,mysql依赖,junit依赖

2)在<build>中加入资源

4.创建实体类Student,定义属性,属性要和student表中的列名保持一致

5.创建Dao接口,定义操作数据库的方法

6.创建xml文件(mapper文件),写sql语句

mybatis框架推荐是把sql语句和java代码分开

mapper文件:定义和dao接口在同一目录,一个表一个mapper文件。

7.创建mybatis的主配置文件(xml文件),有一个,放在resources目录下

1)定义创建连接实例的数据源(DataSource)对象

2)指定其他mapper文件的位置。

8.创建测试的内容

1)可以使用main方法,测试MyBatis访问数据库

2)也可以使用junit访问数据库

概念

1.自动提交:当你的sql语句执行完毕后,提交事务,数据更新操作直接保存到数据库

2.手动提交事务:在需要提交事务的位置,执行方法,提交事务或者回滚事务。

主要的类的介绍

1.Resources:mybatis中的一个类,负责读取主配置文件信息

InputStream in= Resources.getResourceAsStream(config);


2.SqlSessionFactoryBuilder:创建SqlSessionFactory对象,

SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//创建SqlSessionFactory对象
SqlSessionFactory factory=builder.build(in);


3.SqlSessionFactory:重量级对象,程序创建这个对象耗时比较长,使用资源比较多,在整个项目中,有一个就够了。

SqlSessionFactory:接口,接口实现类:DefaultSqlSessionFactory

SqlSessionFactory作用:获取SqlSession对象,SqlSession sqlSession=factory.openSession();

openSession()方法说明:

1)openSession():无参数的,获取的是非自动提交事务的SqlSession对象

2)openSession(Boolean):openSession(true) 获取自动提交事务的SqlSession

openSession(false)非自动提交事务的SqlSession对象。

4.SqlSession:是一个接口,定义了操作数据的方法,例如selectOne(),selectList(),insert(),update(),delete(),commit(),rollback()。

SqlSession接口的实现类DefaultSqlSession

SqlSession使用要求:SqlSession对象不是线程安全的,需要在方法内部使用,在执行sql语句之前,使用openSession()获取SqlSession对象。在执行完sql语句后,需要关闭它,执行SqlSession.close(),这样能保证他的使用是线程安全的。

SqlSession使用步骤:

1.在方法的内部,执行sql语句之前,先获取sqlSession对象

2.调用SqlSession的方法,执行sql语句

3.关闭SqlSession对象,执行SqlSession.close()

SqlSession的作用是提供了大量的执行sql语句的方法:

selectOne:执行sql语句,最多得到一行记录,多余一行是错误的
selectList:执行sql语句,返回多行数据
selectMap:执行sql语句,得到一个Map结果
insert:执行insert语句
update:执行update语句
delete:执行删除语句
commit:提交事务
rollback:回滚事务


dao代理

mybatis提供代理:mybatis创建Dao接口的实现类对象,完成sql语句的执行。mybatis创建一个对象代替你自己的dao实现类功能。

使用mybatis代理要求:

1)mapper文件中的namespace一定是dao接口的全限定名称

2)mapper文件中标签的id是dao接口方法的名称。

mybatis代理实现方式:使用SqlSession对象的方法getMapper(dao.class)

例如:现在有StudentDao接口,接口中有方法selectStudentById,相关代码为:

SqlSession sqlSession=MyBatisUtils.getSession();//MyBtisUtils.getSession()方法为自己写的工具类:获取sqlSession方法的工具类
StudentDao dao=sqlSession.getMapper(StudentDao.class);
Student student=dao.selectStudentById(1001);//1001为学生学号


理解参数

理解参数:通过java程序把数据传入到mapper文件中的sql语句,参数主要是指dao接口方法的形参。

parameterType

parameterType表示的是参数的类型,指定dao方法的形参数据类型。这个属性值可以使用java类型的全限定名称或者mybatis定义的别名

dao接口方法是一个简单类型的参数

简单类型:包括java基本数据类型和String

Student selectStudentByEmail(String email);
<!--mapper文件获取这个参数值,使用#{任意字符}-->
<select id="selectStudentByEmail" resultType="com.hao.entity.Student">
select id,name,email,age from student where email=#{email}
</select>


dao接口方法有多个简单类型的参数

@param:命名参数,在方法的形参前面使用的,定义参数名,这个名称可以用在mapper文件中。

dao接口,方法的定义

/*
多个简单类型的参数
使用@param命名参数,注解是mybatis提供的
使用位置:在形参定义的前面
属性:value自定义的参数名称(不过自定义的名字要与mapper文件中的变量一样)
*/
List<Student> selectStudentsByNameOrAge(@Param("myname") String name, @Param("myage") int age);
<!--当使用了@Param命名后,例如@Param("myname"),则在mapper中使用#{命名的参数},name=#{myname},即@Param中的参数值与name里面的参数值一致-->
<select id="selectStudentsByNameOrAge" resultType="com.hao.entity.Student">
select id,name,email,age from student where name=#{myname} or age=#{myage}
</select>


使用对象作为参数

方法的形参是一个java对象,这个java对象表示多个参数。使用对象的属性值作为参数使用

java对象的定义:

public class Student {
private int id;
private String name;
private String email;
private int age;
具有set和get方法;
}


dao接口:

//按照对象查询学生
List<Student> selectStudentsByObject(Student student);


mapper文件:

<!--
一个java对象作为方法的参数,使用对象的属性作为参数值使用
简单的语法:#{属性名},mybatis调用此属性的get方法获取属性值。
-->
<select id="selectStudentsByObject" resultType="com.hao.entity.Student">
select id,name,email,age from student where name=#{name} or age=#{age}
</select>


任意对象都可以使用,只要有set方法

@Test
public void selectStudentsByObject(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao dao=sqlSession.getMapper(StudentDao.class);
Student student=new Student();
student.setAge(18);
student.setName("李白");
List<Student> students=dao.selectStudentsByObject(student);
students.forEach(stu-> System.out.println(stu));
sqlSession.close();
}


#和$的区别

#占位符

语法:#{字符}

mybatis处理#{} ,在jdbc中使用的对象是PrepareStatement对象

#{}的特点:

1)使用的PrepareStatement对象,执行sql语句,效率高。

2)使用的PrepareStatement对象,能避免sql语句,SQL语句执行的更安全。

3)#{}常常作为列值使用的,位于等号的右侧,#{}位置的值和数据类型有关的。

$占位符

语法:${字符}

myvatis执行${}占位符的sql语句,在jdbc中相当于使用Statement对象

${}的特点:

1)使用Statement对象执行sql语句,效率低

2)${}占位符的值,使用字符串连接方式,有sql注入的风险,有代码安全的问题

3)${}数据是原样使用的,不会区分数据类型

4)${}常用作表名或者列名,在能保证数据安全的情况下使用${}

封装MyBatis输出结果

封装输出结果:MyBatis执行sql语句,得到Resultset,转为java对象

resultType

resultType属性:在执行select时使用,作为<select>标签的属性出现的

resultType:表示结果类型,mysql执行sql语句后得到的java对象类型。

1.resultType表示简单类型:

dao方法:

long countStudent();


mapper文件:

<!--执行sql语句,得到的是一个值(一行一列)-->
<select id="countStudent" resultType="java.lang.Long">
select count(*) from student
</select>


2.resultType表示map结构

执行sql得到一个Map数据结构,mybatis执行sql,把ResultSet转为map,

sql执行结果,列名做map的key,列值作为value

sql执行的是一行记录,转为map结构是正确的。

dao接口返回是一个map,sql语句最多能获取一行,多余一行就是错误的

Map<Object,Object> selectMap(@Param("stuid") Integer id);
<select id="selectMap" resultType="java.util.Map">
select * from student where id=#{stuid}
</select>


resultType:使用的是java类型的全限定名称,表示的意思是mybatis执行sql,把ResultSet中的数据转为Student类型的对象,MyBatis会做以下的操作:

1:调用com.hao.entity.Student的无参构造方法,创建对象。

Student student =new Student();

2.同名的列赋值给同名的属性

student.setId(rs.getInt("id"));

student.setName(rs.getName("name"));

3.得到java对象,如果dao接口返回的是List集合,mybatis把student对象放入到List集合。

resultMap

resultMap:结果映射,自定义列名和java对象属性的对应关系,常用在列名和属性名不同的情况。

用法:

1.先定义resultMap标签,指定列名和属性名称对应关系

2.在select标签使用resultMap属性,指定上面定义的resultMap的id值

<!--使用resultMap定义列和属性的关系-->
<!--
定义resultMap
id:给resultMap的映射关系起个名称,唯一值
type:java类型的全限定名称
-->
<resultMap id="aaa" type="com.hao.entity.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属性,指定映射关系的id-->
<select id="selectStudentsByObject2" resultMap="aaa">
select id,name,email,age from student where name=#{name} or age=#{age}
</select>


MyBatis自定义别名

mybatis提供的对java类型定义简短,好记名称

自定义别名的步骤:

1)在mybatis主配置文件中,使用typeAliases标签声明别名

2)在mapper文件中,resultType=“别名”

3)声明别名,在主配置文件中声明别名。

第一种语法格式:

type:java类型的全限定名称(自定义类型)

alias:自定义别名

优点:别名可以自定义

缺点:每个类型必须单独定义

<typeAliases>
<typeAlias type="com.hao.entity.Student" alias="stu" />
</typeAliases>


第二种语法格式:

name:包名,mybatis会把这个包中所有类名作为别名(不用区分大小写)

优点:使用方便,一次给多个类定义别名

缺点:别名不能自定义,必须是类名,并且如果不同的包下有相同的类名,可能会出现一些错误

<typeAliases>
<package name="com.hao.entity" />
</typeAliases>


数据库的列名和java对象属性名称不一样的解决方式

1)使用resultMap:自定义列名和属性名

2)使用resultType:使用列别名,让别名和java对象属性一样

动态sql

动态sql:同一个dao的方法,根据不同的条件可以表示不同的sql语句,主要是where部分有变化。

主要通过使用mybatis提供的标签,实现动态sql的能力,主要学习if,where,foreach,sql,使用动态sql的时候,dao方法的形参使用java对象。

什么情况下使用动态sql:多条件查询的时候使用动态sql。

if标签

语法格式:

<if test="boolean判断结果">
sql代码
</if>
<!--多个if可以同时存在-->


在mapper文件中:

<select id="selectStudent" resultType="com.hao.entity.Student">
<!--主sql-->
select * from student
<!--部分sql,如果部分sql满足条件,将会把部分sql加入到主sql当中执行。-->
<if test="条件">
sql语句
</if>
</select>


示例:

List<Student> selectIf(Student student);
<select id="selectIf" resultType="com.hao.entity.Student">
select * from student
<!--为了使语句严谨,要加上1=-1.....-->
where 1=-1
<if test="name!=null and name!=''">
or name=#{name}
</if>
<if test="age>0">
or age=#{age}
<!--如果用大于号或者小于号要用实体类符号代替-->
</if>
</select>


Test

@Test
public void testSelectIf(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao dao=sqlSession.getMapper(StudentDao.class);
Student student=new Student();
//student.setName("凯");
student.setAge(18);
List<Student> list=dao.selectIf(student);
list.forEach(stu-> System.out.println(stu));
sqlSession.close();
}


where标签

使用if标签时,容易引起sql语句语法错误,使用where标签解决if产生的语法问题,

使用where标签时,里面是一个或者多个if标签,当有一个if标签判断条件为true,where标签会转为WHERE关键字附加到sql语句的后面,如果if没有一个条件为true,忽略where和里面的if。

语法规则:
<where>
<if test="条件1">sql语句1</if>
<if test="条件2">sql语句2</if>
</where>
<select id="selectWhere" resultType="com.hao.entity.Student">
select * from student
<where>
<if test="name!=null and name!=''">or name=#{name}</if>
<if test="age>0">or age>#{age}</if>
</where>
</select>


foreach

使用foreach可以循环数组,list集合,一般使用在in语句中。

语法格式:

<foreach collection="集合类型" open="开始的字符" close="结束的字符" item="集合中的成员" separator="集合成员之间的分隔符">
#{item的值}
</foreach>
<!--
标签属性:
collection:表示循环的对象是数组还是list集合。如果dao接口方法的形参是数组,collection="array",如果dao接口形参是List,collection="list"
open:循环开始时的字符
close:循环结束时的字符
item:集合成员,自定义变量
separator:集合成员之间的分隔符
#{item}:获取集合成员的值
-->


foreach第一种方式,循环简单类型的List

dao接口:

List<Student> selectForeachOne(List<Integer> idlist);


映射文件;

<select id="selectForeachOne" resultType="com.hao.entity.Student">
select * from student where id in
<foreach collection="list" open="(" close=")" separator="," item="myid">
#{myid}
</foreach>
</select>


测试文件:

@Test
public void testSelectForeachOne(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao dao=sqlSession.getMapper(StudentDao.class);
List<Integer> idlist=new ArrayList<>();
idlist.add(1001);
idlist.add(1002);
idlist.add(1003);
List<Student> students=dao.selectForeachOne(idlist);
students.forEach(stu-> System.out.println(stu));
sqlSession.close();
}


foreach第一种方式,循环对象类型的List

dao接口:

List<Student> selectForeachTwo(List<Student> studentList);


映射文件:

<select id="selectForeachTwo" resultType="com.hao.entity.Student">
select * from student
<if test="list!=null and list.size>0">
where id in
<foreach collection="list" open="(" close=")" separator="," item="stu">
#{stu.id}
</foreach>
</if>
</select>


测试文件:

@Test
public void testSelectForeachTwo(){
SqlSession sqlSession=MyBatisUtils.getSqlSession();
StudentDao dao=sqlSession.getMapper(StudentDao.class);
List<Student> stuList=new ArrayList<>();
Student stu1=new Student();
stu1.setId(1001);
Student stu2=new Student();
stu2.setId(1002);
stuList.add(stu1);
stuList.add(stu2);
List<Student> students=dao.selectForeachTwo(stuList);
students.forEach(stu-> System.out.println(stu));
sqlSession.close();
}


sql标签:代码片段

sql标签表示一段sql代码,可以是表名,几个字段,where条件都可以,可以在其他地方复用sql标签的内容,

使用方式:

1)在mapper文件中定义sql代码片段<sql id="唯一字符串"> 部分sql语句</sql>
2)在其他的位置,使用include标签引用某个代码片段<include refid="代码片段的id" />


MyBatis配置文件

mybatis配置文件分为两大类:

1)mybatis主配置文件:

提供mybatis全局设置的,包含的内容有:日志,数据源,mapper文件的位置。

2)mybatis的mapper文件。

写sql语句的,一般是一个表一个mapper文件。

settings部分

settings是mybatis的全局设置,影响整个mybatis的运行,这个设置一般使用默认值就可以了。

<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>


typeAliase别名

第一种语法格式:

type:java类型的全限定名称(自定义类型)

alias:自定义别名

优点:别名可以自定义

缺点:每个类型必须单独定义

<typeAliases>
<typeAlias type="com.hao.entity.Student" alias="stu" />
</typeAliases>


第二种语法格式:

name:包名,mybatis会把这个包中所有类名作为别名(不用区分大小写)

优点:使用方便,一次给多个类定义别名

缺点:别名不能自定义,必须是类名,并且如果不同的包下有相同的类名,可能会出现一些错误

<typeAliases>
<package name="com.hao.entity" />
</typeAliases>


配置环境

environments:环境标签,在它里面可以配置多个environment
属性:default,必须是某个environment的id属性值,表示mybatis默认连接的数据库。
environment:表示一个数据库的连接信息。
属性:id自定义的环境标识,唯一值
transactionManager:事务管理器
属性:type表示事务管理器的类型。
属性值:1)JDBC:使用Connection对象,由mybatis自己完成事务的处理。
2)MANAGED:管理,表示把事务的处理交给容器实现(由其他软件完成事务的提交,回滚)
dataSource:数据源,创建Connection对象,连接数据库。
属性:type数据源的类型
属性值:1)POOLED,mybatis会在内存中创建PooledDataSource类,管理多个Connection连接对象,使用的连接池。
2)UNPOOLED,不使用连接池,mybatis创建一个UnPooledDataSouurce这个类,每次执行sql语句先创建Connection对               象,再执行sql语句,最后关闭连接。
3)JNDI,java的命名和目录服务
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/springdb"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>


使用数据库属性配置文件

需要把数据库的配置信息放到一个单独文件中,独立管理,这个文件扩展名是properties.在这个文件中,使用自定义的key=value的格式表示数据。

使用步骤:

1.在resources目录中,创建xxxx.properties

2.在文件中,使用key=value的格式定义数据

例如:jdbc.url=jdbc:mysql://localhost:3306/springdb

3.在mybatis主配置文件,使用property标签引用外部的属性配置文件

语法:
<properties resource="properties文件路径"/>


4.在使用值的位置,使用${key}获取key对应的value(等号右侧的值)

<properties resource="jdbc.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springdb
jdbc.username=root
jdbc.password=123456


mapper标签

使用mapper指定其他mapper文件的位置,mapper标签使用的格式有两个常用的方式。

<mappers>
<!--
第一种方式:resources="mapper文件的路径"
优点:文件清晰,加载的文件是明确的,
文件的位置比较灵活。
缺点:文件比较多时,代码量会比较大。
-->
<mapper resource="com/hao/dao/StudentDao.xml"/>
<!--
第二种方式:使用<package>
name:包名,mapper文件所在的包名。
特点:把这个包中的所有的mapper文件,一次加载
使用要求:
1.mapper文件和dao接口在同一目录
2.mapper文件和dao接口名称完全一样
-->
<package name="com.hao.dao"/>
</mappers>


PageHelper

PageHelper做数据分页,在select语句后面加入分页的SQL内容,如果使用的是mysql数据库,他就是在select * from student 后面加入limit语句。

使用步骤:

1)加入PageHelper依赖

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>


2)在mybatis主配置文件,加入plugin声明

在<environments>之前加入
<plugins>
<plugin interceptor="com.github.pagehelper.PageIntercapter"/>
</plugins>


3)在select语句之前,调用PageHelper.startPage(页码,每页大小)

PageHelper.startPage(1,3);