什么是框架?
盖高楼,框架结构。
框架结构就是高楼的主体,基础功能。
把很多基础功能已经实现了(封装了)。
在基础语言之上,对各种基础功能进行封装,方便开发者,提高开发效率。
mybatis:对jdbc进行封装
spring:对整个java后端架构进行管理的
springweb:对servlet层进行封装
springboot:对spring框架的搭建进行封装
Mybatis
介绍:
mybatis原来是Apache下main的一个开源项目,名为ibatis
后转移到谷歌旗下,改名为mybatis
mybatis是一个优秀的数据持久层框架(数据持久层:dao层 数据访问层)
mybatis是对jdbc进行的封装,避免了jdbc中手动设置参数,手动映射结果的操作
mybatis将jdbc中的接口进行封装,提供了他自己的类和接口实现
mybatis可以使用xml配置和注解的方式,将数据库中记录自动映射到java对象中,是一种ORM实现(对象关系映射)将可以自动将数据映射到对象中的这种框架,也称为orm框架
mybatis还提供了动态sql 和 数据缓存
mybatis搭建
Mybatis 中文官网 mybatis – MyBatis 3 | 简介
1、创建一个maven项目
2、在pom.xml导入mabatis依赖的jar
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
3、创建一个全局的mybatis配置文件
在main文件夹中的recources里面创建一个名为mybatis.xml文件
在mybatis.xml文件中配置核心全局相关信息等操作
配置数据库连接
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTDConfig3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置数据库连接相关信息-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--type="POOLED" 使用数据库连接池功能,默认创建10个连接对象,减少频繁创建销毁连接对象-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdb?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
4、创建数据库,创建表,准备数据
在mysql中创建例如名为ssmdb的数据库,在此库中创建名为admin的表
5、创建一个访问接口,定义方法
在java中先创建好每个层所需要的包
然后在dao层中创建AdminDao的接口
public interface AdminDao {
Admin findAdminById(int id);
}
6、创建接口对应的sql映射文件,编写sql
依旧在recources里面先创建名为mappers的文件夹(里面都放着一些sql映射文件),在mappers里创建一个名为AdminMapper.xml文件并编写sql
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTDMapper3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "com.ffyc.mabatispro.dao.AdminDao">
<select id="findAdminById" parameterType="int" resultType="com.ffyc.mybatis.model.Admin">
select * from admin where id = #{id}
</select>
</mapper>
再在mybatis.xml文件中将AdminMapper.xml文件注册配置
<!--注册映射文件-->
<mappers>
<mapper resource="mappers/AdminMapper.xml"></mapper>
</mappers>
7、测试mybatis
单表查询
创建一个test类
public class Test1 {
public static void main(String[] args) throws IOException {
//1.mybatis读取配置文件
Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
//2.创建 SqlSessionFactory,负责创建SqlSession对象(连接数据库的会话对象,类似Connection)
//sqlSessFactory对象也是只需要创建一个,创建后不需要销毁
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
//3.创建SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//4.创建接口的代理对象
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
//5.调用
Admin admin = adminDao.findAdminById(1);//让代理对象帮我们调用映射文件在与此接口中相同名称的方法
System.out.println(admin);
//6.关闭会话对象
sqlSession.close();//关闭与数据库的连接对象
}
}
控制台所展现的是实现了日志功能后的结果(后面有日志功能的配置)
7.1增加
在AdminDao中写方法
void insertAdmin(Admin admin);
在AdminMapper.xml中写sql映射语句
<!--
useGeneratedKeys="true" 返回自增主键
keyProperty="id" 定义接收属性
keyColumn="id" 定义主键列
-->
<insert id="insertAdmin" parameterType="Admin" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into admin(account,password,gender) values (#{account},#{password},#{gender})
</insert>
@Test
public void insert(){
Admin admin = new Admin();
admin.setAccount("ccc");
admin.setPassword("123");
admin.setGender("男");
SqlSession sqlSession = MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
adminDao.insertAdmin(admin);//保存数据后端,需要立刻拿到这条数据在数据库的主键
sqlSession.commit();
sqlSession.close();
}
@Test:单元测试,程序员使用的测试方式,以方法为单位进行测试 使用junit组件实现单元测试
在maven中配置junit组件
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>provided</scope>
</dependency>
sqlSession.commit();//提交数据库事务,当我们的程序代码执行没有任何问题时,再向数据库发送提交事务操作,数据库真正执行sql,出现异常则不提交事务(新增,修改,删除完毕后,都需要手动提交事务)
/*
数据库事务:是数据库的一种管理的机制,是对一次连接数据库过程的管理
保证一次操作中,执行的多条sql,要么都成功执行,要么都不执行
提交事务 数据库才会真正的在数据库执行这一次操作中的多条sql
*/
7.2删除
void deleteAdmin(int id);
<delete id="deleteAdmin" parameterType="int">
delete from admin where id = #{id}
</delete>
7.3修改
void updateAdmin(Admin admin);
<update id="updateAdmin" parameterType="Admin">
update admin set account=#{account},password=#{password} where id = #{id}
</update>
7.4查找
Admin findAdminByAccount(String account);
<select id="findAdminByAccount" parameterType="string" resultType="Admin">
select * from admin where account = #{account}
</select>
搭建补充:
1、在idea中安装mybatisX插件
在setting中找到MybatisX插件并下载下来,若没显示相关图标重启Idea即可
2、数据库连接池
链接数据库 每次访问数据库时候,创建一个Connection,用完关闭,但是访问量大了之后,每次都要创建新的连接对象,用完关闭,比较耗时
使用数据库连接池,在池(集合)中先创建一些连接对象
用户访问时,就直接在池中获取一个连接对象
用完不销毁,还回到池中
这样就减少频繁创建销毁连接对象
<dataSource type="POOLED">
<!--type="POOLED" 使用数据库连接池功能,默认创建10个连接对象,减少频繁创建销毁连接对象-->
3、配置日志功能
<!--配置日志功能-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
4、为类配置别名
<!--为类配置别名-->
<typeAliases>
<!--<typeAlias type="com.ffyc.mybatispro.model.Admin" alias="Admin"></typeAlias>-->
<package name="com.ffyc.mybatispro.model"/>
</typeAliases>
对象映射
如果表中的类名与类中的属性名完全相同,mybatis会自动将查询结果封装 到POJO对象中.
如果java中使用标准驼峰命名,数据库中使用下划线连接命名,可以开始全局 设置实现自动转换
<!--设置驼峰命名自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
#{}:是占位符,是采用预编译方式向sql中传值,可以防止sql注入,如果我们往sql中传值,就使用#{}
${}:是将内容直接拼接到sql语句中,一般不用于向sql中传值,用于向sql中动态传递列名
#{}与${}之间的区别:
1、底层实现不同
#{} 采用预编译方式,防止sql注入,更加安全
${} 采用字符串拼接,直接将值拼接到sql中
2、使用场景不同
#{} 一般我们用于向sql中的列传值
${} 一般用于向sql动态传递列名
例如:排序时,order by 后面的列名是可以改变
select时,后面的列名也可以自由选择
List<Admin> findAdmins1(@Param("Column") String Column);
<!--${}的实用案例-->
<select id="findAdmins1" resultType="Admin" parameterType="string">
select * from admin order by ${Column} desc
</select>
@Test
public void find3 (){
SqlSession sqlSession = MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
adminDao.findAdmins1("id");//${}的用法
sqlSession.commit();
sqlSession.close();
}
多表查询
关联查询(一对一)
以学生和专业为例
方式一:直接多表关联查询
(1)当查询一个学生和他所在的专业时(返回的为一个学生对象)
<!--
关联查询方式1:直接多表关联查询出我们需要的数据(一对一)
-->
<select id="findStudentById" resultMap="studentMap">
select
s.id,
s.num,
s.name,
s.gender,
m.name mname
from student s inner join major m on s.majorid = m.id where s.id = #{id}
</select>
<!--对关联查询到的学生信息进行自定义映射封装-->
<resultMap id="studentMap" type="student">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
<!--映射关联数据 专业名称 首先会创建一个Major对象,然后将专业名称封装到Major对象,最后将Major对象封装到Student对象中去-->
<association property="major" javaType="Major">
<result column="mname" property="name"></result>
</association>
</resultMap>
(2)查询所有学生(返回的为多个学生对象,应创建一个学生类集合存放)可以共用resultMap
<select id="findStudents" resultMap="studentMap">
select
s.id,
s.num,
s.name,
s.gender,
m.name mname
from student s inner join major m on s.majorid = m.id
</select>
方式二:嵌套查询
<!--
关联查询方式2:嵌套查询 先查询主表(学生表)(一对一)
-->
<select id="findStudentById1" resultMap="studentMap1">
select id,num,name,gender,majorid from student where id = #{id}
</select>
<!--嵌套查询学生关联的专业-->
<select id="findMajorById" resultType="Major">
select name from major where id = #{majorid}
</select>
<resultMap id="studentMap1" type="Student">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
<!--封装关联表数据-->
<association property="major" javaType="Major" select="findMajorById" column="majorid"></association>
</resultMap>
在一对一查询时,resultMap中要用到<association>标签用于想要关联的表上
<association>中:property="" 表示所要映射到的哪个成员变量上
javaType="" 表示所映射的成员变量的数据类型
select="" 表示映射的成员变量的查询语句
column="" 表示映射的成员变量要以上一个查询的某一列数据作为查询条件
关联查询(一对多)
方式一:直接多表关联查询
(1)查询一个专业时,可以将查询到的多个学生封装到一个集合中
<select id="findMajorById" resultMap="majorMap">
select
m.id,
m.name,
s.num,
s.name sname
from major m inner join student s on m.id = s.majorid where m.id = #{id}
</select>
<resultMap id="majorMap" type="Major">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<!--将查询关联到的多个结果封装到集合-->
<collection property="students" javaType="List" ofType="Student">
<result column="num" property="num"></result>
<result column="sname" property="name"></result>
</collection>
</resultMap>
(2)查询所有专业和每个专业的学生
有些专业目前还没有学生,所以关联要用left join
<select id="findMajors" resultMap="majorMap">
select
m.id,
m.name,
s.num,
s.name sname
from major m left join student s on m.id = s.majorid
</select>
方式二:分成两次查询
<select id="findMajors1" resultMap="majorMap1">
select id,name from major
</select>
<select id="findStudents" resultType="Student">
select num,name from student where majorid = #{id}
</select>
<resultMap id="majorMap1" type="Major">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<collection property="students" javaType="List" ofType="Student" select="findStudents" column="id"></collection>
</resultMap>
在一对多查询时,resultMap中要用到<collection>标签用于想要关联的表上
<collection>标签中:property="" 表示所要映射到的哪个成员变量上
javaType="" 表示映射的成员变量的类型
ofType="" 表示映射的成员变量的类型里的数据类型
select="" 表示映射的成员变量的查询语句
column="" 表示映射的成员变量要以上一个查询的某一列数据作为查询条件
动态SQL问题
以前在写sql查询语句时,难免会遇到一些多个条件一起查询的时候,当我们将每个条件都输入时才会出现一条结果。但如果在其中一个条件不成立时,就要修改sql语句,非常痛苦!
在之前的动态sql语句中,例如:(借助Mybatis去展示原始动态sql问题)
<!--原始动态sql-->
<select id="teachers" resultType="Teacher">
select * from teacher
where
<if test="num!=null">
num = #{num}
</if>
<if test="name!=null">
and name = #{name}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</select>
当其中一个条件不成立或者多个条件不成立就会出现以下结果:
你的sql语句参差不齐,要么多关键字,要么关键字之间无sql语句
在之前对动态sql语句解决的办法就是在where后面自己加一个判断条件,让它永远成立,保证不管条件有没有成立,sql语句优先保证无误
<!--以前解决动态sql问题的解法-->
<select id="teachers" resultType="Teacher">
select * from teacher
where 1=1
<if test="num!=null">
num = #{num}
</if>
<if test="name!=null">
and name = #{name}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</select>
现在,在Mybatis里就增加了多种标签用于解决动态SQL问题
1、if标签
test属性条件成立 执行if标签体,不成立就不执行
<select id="teachers" resultType="Teacher">
select * from teacher
where 1=1
<if test="num!=null">
num = #{num}
</if>
<if test="name!=null">
and name = #{name}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</select>
2、where标签
当where标签 if语句有条件成立时,就会动态添加where关键字 还可以删除where后面紧跟着的关键字,例如and、or
<select id="teachers" resultType="Teacher">
select * from teacher
<where>
<if test="num!=null">
num = #{num}
</if>
<if test="name!=null">
and name = #{name}
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</where>
</select>
3、trim标签
当条件判断有成立时,可以自定义前缀关键字和后缀关键字
prefix="" 自定义关键字
prefixOverrides="" 覆盖指定的关键字
<select id="teachers" resultType="Teacher">
select * from teacher
<trim prefix="where" prefixOverrides="and|or">
<if test="num!=null">
num = #{num}
</if>
<if test="name!=null">
name = #{name}
</if>
<if test="gender!=null">
gender = #{gender}
</if>
</trim>
</select>
4、foreach标签
<select id="findTeacher" resultType="Teacher">
select
<foreach item="col" collection="list" separator=",">
${col}
</foreach>
from teacher
</select>
<foreach>标签中:item="" 表示col变量存储遍历到当前的值
collection="" 表示遍历集合类型
特殊字符的转义
在编写XML文件时,有些内容可能不想让解析引擎解析执行,而是当作原始内容处理。
遇到此种情况,可以把这些内容放在CDATA区里,对于CDATA区域内的内容,XML解析程序不会处理,而是直接原封不动的输出。
语法:<![CDATA[ 内容 ]]>
<![CDATA[
<itcast>
<br/>
</itcast>
]]>
对于一些单个字符,若想显示其原始样式,也可以使用转义的形式予以处理。
特殊字符 | 替代符号 |
& | & |
< | < |
> | > |
" | " |
, | ' |
缓存
数据缓存 让数据离我们的执行程序更近。让程序能够更快速的获取到数据
手机缓存,浏览器缓存......cpu缓存等等
有缓存,查询下的查询流程
先从缓存中查询,缓存中如果没有,去数据库查询,查询到后把数据放到缓存中,下次直接从缓存中获取
mybatis一级缓存
mybatis一级缓存,默认是SqlSession级别的,在同一个SqlSession中查询到数据先缓存到SqlSession对象中
第二次查询数据时,先从SqlSession中查询,如果有直接返回,没有,再去查询数据库
一级缓存生命周期:
开始于SqlSession创建
结束于SqlSession关闭。如果期间执行了新增、修改、删除操作也会清空当前SqlSession对象中的缓存数据
mybatis二级缓存
二级缓存是SqlSessionFactory级别的,可以让多个SqlSession共享数据
mybatis默认没有开启二级缓存,使用时需要配置开启
如果开启了二级缓存,当SqlSession关闭时,会将一级缓存中的数据存储到二级缓存中。
其他的SqlSession就可以查询二级缓存之前SqlSession查询的数据
二级缓存的配置:
1、在mybatis.xml配置文件中设置二级缓存的开关
<!--全局二级缓存开关-->
<setting name="cacheEnabled" value="true"/>
2、在对应的模型类里去实现 Serializable 接口
import java.io.Serializable;
public class Teacher implements Serializable
3、在SQL 映射文件中添加一行:
<cache></cache>
<!--
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
-->