学习Mybatis
1.mybatis的介绍
mybatis原是apache的一个开源项目,2010年转投谷歌,从ibtais3.0开始改名为mybatis
mybatis是一个优秀的数据持久层框.
数据持久层(dao)
servlet(web) 接收请求, 调用其他java代码处理, 响应
service 业务逻辑层
dao 数据访问/持久 3层架构
mybatis是对jdbc进行轻量级的封装.
提供专门xml文件来进行配置,以及可以自动的对查询结果进行封装,
是一个ORM(java对象与数据库表映射)实现的数据持久层的框架.
提供一些自己定义的类和接口来实现功能.
支持动态sql,以及数据缓存.
Mybatis环境搭配以及使用
1).创建一个maven项目,导入mybatis和mysql的jar包
//1.mybatis.jar <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.2</version> </dependency>
//2.mysql.jar <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency>
2.创建一个数据库表,以及一个应的java模型类
package com.ffyc.jdbcpro.model;
public class Admin {
private int id;
private String account;
private String password;
private String adminGender;
public String getAdminGender() {
return adminGender;
}
public void setAdminGender(String adminGender) {
this.adminGender = adminGender;
}
public Admin() {
System.out.println("Admin无参构造方法");
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Admin{" +
"id=" + id +
", account='" + account + '\'' +
", password='" + password + '\'' +
", adminGender='" + adminGender + '\'' +
'}';
}
}
3.创建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">
<!--
mybatis核心配置文件
-->
<configuration>
<properties resource="config.properties"></properties><!--数据库连接池文件-->
<!-- mybatis设置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/><!--日志功能-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--配置类型别名-->
<typeAliases>
<!--<typeAlias type="com.ffyc.jdbcpro.model.Admin" alias="Admin"></typeAlias>-->
<package name="com.ffyc.jdbcpro.model"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED"><!--使用数据库连接池(缓冲池)POOLED/U-->
<property name="driver" value="${driverName}" />
<property name="url" value="${url}" />
<property name="username" value="${uname}" />
<property name="password" value="${upassword}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/AdminMapper.xml"></mapper>
</mappers>
</configuration>
3.1数据库连接池
数据库连接池(缓冲池)
现在每与数据库交互一次,创建一个数据库连接对象(Connection,SqlSession),用完就关闭,下一次需要,重复这个过程。问题频繁创建销毁对象需要开销的。
数据库连接池的思想:
可以在启动时设置一个容器,在里面初始化还一些数据库连接对象,请求到来时,可以不用每次都创建销毁,可以重复使用,减少了频繁创建销毁连接对象的开销
一般的设置:
初始连接对象的数量
最大连接对象的数量,
最大的等待时间
3.2设计数库连接池
driverName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/ssm_db?serverTimezone=Asia/Shanghai
uname=root
upassword=root
4.配置sql映射文件
<?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="接口地址">
定义 sql 语句
</mapper>
5.定义接口,并且在映射文件中进行访问
public interface AdminDao {
// 接口中的方法名,与xml中的标签id名相同
// 接口中方法参数类型 与xml中的parameterType里面的类型相同
// 接口中方法返回值类型 与xml标签中resultType类型相同
Admin findAdminById(int id);
}
<?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">
<!--namespace接口完整地址-->
<mapper namespace="com.ffyc.jdbcpro.dao.AdminDao">
<select id="findAdminById" resultType="Admin" parameterType="int">
select id,account,password,admin_gender from admin where id = #{id}
</select>
</mapper>
6.测试mybatis
package com.ffyc.jdbcpro.test;
import com.ffyc.jdbcpro.dao.AdminDao;
import com.ffyc.jdbcpro.model.Admin;
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 java.io.IOException;
import java.io.Reader;
public class TestAdmin {
public static void main(String[] args) throws IOException {
/*
配置核心文件
*/
Reader reader = Resources.getResourceAsReader("mybatis.xml");
/*
创建sessionFactory
SqlSessionFactory封装所有的配置信息,
SqlSessionFactory负责生成一个数据库链接的会话对象SqlSession对象
SqlSessionFactory创建开销比较大,所以整个项目中创建一次即可,不用销毁
*/
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
// new SqlSessionFactoryBuilder().build(reader,"word");
/*
创建SqlSession session会话,一次与数据库交互,类似于之前使用的Connection
SqlSessionFactory中的openSession方法用来创建一个SqlSession对象,默认无参的默认设置事物事物为false(手动的)
*/
SqlSession sqlSession = sessionFactory.openSession();
// Admin admin1 = sqlSession.selectOne("com.ffyc.findAdminById",1);
// System.out.println(admin1);
/*
现在mybatis
创建访问接口的代理对象
*/
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
/*
使用代理对象访问接口中对应方法,本质是调用的是接口对应的sql映射文件中的sql
*/
Admin admin = adminDao.findAdminById(1);
System.out.println(admin);
/*
关闭与数据库连接对象
*/
sqlSession.commit();
sqlSession.close();
}
}
7进行封装简化代码
package com.ffyc.jdbcpro.util;
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 java.io.IOException;
import java.io.Reader;
public class MybatisUtil {
static SqlSessionFactory sqlSessionFactory= null;
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis.xml");
} catch (IOException e) {
e.printStackTrace();
}
/*
创建sessionFactory
SqlSessionFactory封装所有的配置信息,
SqlSessionFactory负责生成一个数据库链接的会话对象SqlSession对象
SqlSessionFactory创建开销比较大,所以整个项目中创建一次即可,不用销毁
*/
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
package com.ffyc.jdbcpro.test;
import com.ffyc.jdbcpro.dao.AdminDao;
import com.ffyc.jdbcpro.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.io.IOException;
public class TestAdmin1 {
public static void main(String[] args) throws IOException {
SqlSession sqlSession = MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
adminDao.findAdminById(1);
sqlSession.close();
}
}
8.Sql进行增删改查
增加
<insert id="唯一标识" useGeneratedKeys="把新增加的主键赋值到自己定义的
keyProperty " keyProperty=“ 接收主键的属性 parameterType="参数类型">
insert into user(userName,userAge)values(#{userName},#{userAge})
</insert>
删除
<delete id="唯一标识" parameterType="参数类型">
delete from admin where id= #{id}
</delete>
修改
<update id="唯一标识" parameterType=“参数类型">
update admin set account= #{account},password= #{password} where id= #{id}
</update>
查询
<select id="唯一标识" resultType="返回结果集类型">
select * from admin where id= #{id}
</select>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<select id="findUserInfoById" parameterType="int"resultType="User">
select * from admin where id=#{id}
</select>
组装查询

嵌套查询
<!-- 指定自动映射策略 NONE:无论是否嵌套,都不会自动映射 PARTIAL(部分):是mybatis默认值 FULL:无论是否嵌套,都会进行自动映射 --> <setting name="autoMappingBehavior" value="NONE"/>
但是不管是NONE还是FULL结果都不太理想,但是可以进行手动嵌套映射,分为两种
1.把学生关联的宿舍和操作人信息分别封装到宿舍对象和管理员对象中去, 属于嵌套映射
<resultMap id="dormMap" type="Dorm">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<!-- 一个宿舍住了多个学生,需要将多个学生进行映射-->
<collection property="students" javaType="list" ofType="Student">
<result column="name" property="name"></result>
<result column="snum" property="num"></result>
</collection>
</resultMap>
<select id="findDorms" resultMap="dormMap">
SELECT
d.id,
d.num,
s.name,
s.num snum
FROM
dorm d
LEFT JOIN student s
ON d.id = s.dormid
</select>
collection:嵌套结果映射 – 集合可以是 resultMap
元素,或是对其它结果映射的引用处理一对多关联
2.嵌套查询,把一次查询分为多次嵌套
<!--嵌套查询,把一次查询分为多次嵌套-->
<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="dorm" javaType="Dorm" select="findDormById" column="dormid"></association>
<association property="admin" javaType="Admin" select="findAdminById" column="adminid"></association>
</resultMap>
<select id="findStudentById1" resultMap="studentMap1" parameterType="int">
select id,num,name,
gender,dormid,adminid
from student
where id = #{id}
</select>
<select id="findDormById" parameterType="int" resultType="Dorm">
select num from dorm where id = #{dormid}
</select>
<select id="findAdminById" parameterType="int" resultType="Admin">
select account from admin where id = #{adminid}
</select>
association:嵌套结果映射 – 关联可以是 resultMap
元素,或是对其它结果映射的引用
注解方式进行增删改查
//删除注释
@Delete("delete from student where id = #{id}")
void deleteStudent(int id);
//新增
@Insert("insert into student (num,name,gender)value (#{num},#{name},#{gender})")
void saveStudent(Student student);
//查询
@Select("select * from student where id = #{id}")
@Results(id = "stuMap" ,value = {
@Result(id = true,column = "id",property = "id"),
@Result(column = "num",property = "num"),
})
@ResultMap(value = "stuMap")
Student findStudent(int id);
Mybatis动态Sql
用于实现动态Sql的元素主要有
if,where,trim,set,choose(when,otherwise),foreach
if对传入条件进行判断
where 元素只会在子元素返回任何内容的情况下才插入 “where” 子句。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
trim
<!-- trim
prefix:条件成立时添加指定关键字
prefixOverrides:条件成立时去掉指定关键字
-->
<select id="findStudent" resultType="Student" parameterType="Student">
select id,num,name,gender from student
<trim prefix="where" prefixOverrides="add">
<if test="num!=0">
num = #{num}
</if>
<if test="name!=null">
and name = #{name}
</if>
<if test="gender!=null">
add gender = #{gender}
</if>
</trim>
</select>
choose:多个添加选择一个进行
<!--
choose:
when:
otherwise:
多个条件选择一个
可以有多个when
-->
<select id="findStudent" resultType="Student" parameterType="Student">
select id,num,name,gender from student
<trim prefix="where" prefixOverrides="add">
<choose>
<when test="num!=0">
num = #{num}
</when>
<when test="name!=null">
name = #{name}
</when>
<otherwise>
and gender = #{gender}
</otherwise>
</choose>
</trim>
</select>
set可以动态的根据条件是否成立添加set,可以去除后面得逗号
<update id="updateStudent" parameterType="Student">
update student
<set>
<if test="num!=0">
num = #{num},
</if>
<if test="name!=null">
name =#{name},
</if>
<if test="gender!=null">
gender =#{gender}
</if>
</set>
where id = #{id}
</update>
<delete id="deleteStudent">
delete from student where id in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</delete>
特殊符号处理
xml,html都是标记语言
类似<,>,&这种不能在sql中使用
解决办法:1.通过转义符号替换2.以使用<![CDATA[]]>来包裹特殊字符。
转义字符包括:< < >>" "' &apos ;& &
使用<![CDATA[]]>来包裹特殊字符例如:
<delete id="deleteStudent1">
delete from student where id<![CDATA[<]]>3
</delete>
9.Mybatis一级缓存二级缓存
缓存
手机上的缓存 加载图片..... 存在手机本地(内存(机身内存,运行内存))
浏览器中的缓存 访问一些网页,将一些信息缓存到本地(硬盘)
买票/购票缓存 很多人访问 ---数据缓存(运行内存)--> 数据库
缓存作用:为了让程序更快的访问到数据,同时也是为了减少数据库的访问压力可以让数据缓存到内存,手机内存,客户端硬盘中一级缓存:
mybatis提供一级缓存和二级缓存 默认开启一级缓存,以及缓存时Sqlsession级别的,当第一次查询数据时将查到的结果封装到Sqlsession对象中 在同一个sqlsession中执行相同的二次查询时,直接从sqlsession中获取 一级缓存失效 sqlsession.close();销毁sqlsession对象 sqlSession.clearCache();清除缓存中的数据 新增修改删除会清空缓存数据,flushCache="true"
public void find(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
// student.setNum(111);
student.setName("小红");
List<Student> list = studentDao.findStudent(student);
System.out.println("0000");
sqlSession.clearCache();
student.setNum(10000);
List<Student> list1 = studentDao.findStudent(student);
System.out.println("0000");
student.setId(1);
student.setName("小绿");
student.setNum(10000);
studentDao.updateStudent(student);
sqlSession.commit();
sqlSession.close();
}
二级缓存
mybatis提供一级缓存和二级缓存 二级缓存:sqlSessionFactory级别,可以实现多个sqlSession共享, 当第一次查询到数据后,关闭sqlSession时将数据存入到二级缓存中 二级缓存使用需要进行配置,在setting中配置 <setting name="cacheEnabled" value="true"/><!--全局开启二级缓存--> 在 Mapper 映射文件中添加<cache /> <cache flushInterval="1000"></cache> 模型类实现序列化接口
public void find1(){
SqlSession sqlSession1 = MybatisUtil.getSqlSession();
StudentDao studentDao1 = sqlSession1.getMapper(StudentDao.class);
Student student = new Student();
student.setName("小绿");
List<Student> list1 = studentDao1.findStudent(student);
sqlSession1.close();//当关闭sqlsession时会将数据饭给到二级缓存中
SqlSession sqlSession2 = MybatisUtil.getSqlSession();
StudentDao studentDao2 = sqlSession2.getMapper(StudentDao.class);
student.setName("小绿");
List<Student> list2 = studentDao2.findStudent(student);
sqlSession1.close();
}