持久化层框架-MyBatis基础
1.为什么要学习Mybatis
原因是jdbc存在以下问题:
1.sql写在JAVA代码中,修改sql必须修改代码,耦合度太高
2.代码量多,重复性代码过多
3.手动设置参数,并且如果是查询操作,还需要手动转换结果,麻烦
4.连接资源手动关闭,麻烦【以后和Spring整合就不需要手动关闭连接了】
2.Mybatis是什么
2.1 定义
Mybatis是一个基于ORM的数据库持久化框架
Mybatis底层还是原生的JDBC代码,对JDBC代码的封装
曾用名:iBatis
2.2 什么是框架?
框架(Framework)是一个框子:指其约束性。也是一个架子:指其支撑性,约束性,支撑性。
程序中的框架其实是一系列jar包(一系列.class文件)的组合,要使用框架必须要导包。
框架是一个半成品,并没有解决具体的问题,但是有了框架之后,不需要从零起步,提高了开发效率,要实现具体的功能还是要写代码。
框架通常是为了解决某一个领域的问题而产生,比如解决数据库持久化问题。
综上所述:所谓的框架就是提供的一些支撑结构,通过这些支撑结构,可以相对轻松地解决具体的问题,但是在使用过程中要遵循一定的规范。
2.3 什么是数据库持久化?
将数据保存在数据库中【可掉电设备】
JDBC是一个数据库持久化技术
Mybatis是一个数据库持久化框架
2.4 什么是ORM?
对象关系映射(Object Relational Mapping,简称ORM):是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术;
名称解释:
对象 = Java对象【domain】
关系 = 二维表数据
映射 = 将表中的数据映射到Java对象中
从而在查询的时候就不需要手动转换结果集ResultSet了
查询出来要么是一个实体对象,要么是一个集合
常见的ORM框架
Hibernate
JPA
Mybatis
dao实现 第一天
mapper实现 第二天
3.Mybatis的优势
1.sql语句与代码分离,存放于xml配置文件中,方便维护
2.Mybatis消除了几乎所有的JDBC代码和手工设置参数以及结果集的转换
没有贾琏欲执事、自动设置参数、ORM自动转换结果集(自定义转换)
3.提供XML标签,支持编写动态SQL,代替编写逻辑代码
4.Mybatis入门【难点】
4.1 环境搭建步骤
1.创建工程
2.导包 当前环境所需jar包 mysql的驱动包 mybatis的jar包
3.配置文件
Mybaits主配置文件:连接数据库的配置,加载sql文件/Mapper映射文件
sql文件/Mapper映射文件
4.数据准备
前端数据
后端数据:数据库 - 表 - 实体类/domain,工具类
5.代码开发
导包
maven引入依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IuZ2k8cR-1684896368950)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230522140213099.png)]
总配置文件
创建资源包 resources
在resources中创建jdbc.properties配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root
在resources中创建mybatis-config.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">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
修改模板
<?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>
<!-- 引入属性文件 -->
<properties resource="jdbc.properties"></properties>
<!-- 配置环境:连接相关的信息 -->
<environments default="development">
<!-- 一个environment标签中配置一组连接信息 id是这组连接信息的唯一标识 -->
<environment id="development">
<!-- 使用事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据源:连接池 -->
<dataSource type="POOLED">
<!-- 数据库连接信息及数据源的配置 通过${key}可以引用属性值-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射配置文件:暂时用不到 -->
<!-- <mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers> -->
</configuration>
测试连接
编写测试类
/**
* 测试连接
*/
@Test
public void testConn() {
//获取sessionFactory对象,称为会话工厂,一次连接是一次会话,那会话工厂就可以用来创建连接
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(MyBatisTest.class.getResourceAsStream("/mybatis-config.xml"));
//工厂的openSession方法可以打开一次会话,也就是连接
SqlSession openSession = sqlSessionFactory.openSession();
System.out.println(openSession);
}
4.2 基本使用
准备实体类
package com.hxj.domain;
import java.util.Date;
public class Student {
private Integer sid;
private String snumber;
private String sname;
private Integer age;
private Integer gender;
private Date birthday;
public Student() {
super();
}
public Student(Integer sid, String snumber, String sname, Integer age, Integer gender, Date birthday) {
super();
this.sid = sid;
this.snumber = snumber;
this.sname = sname;
this.age = age;
this.gender = gender;
this.birthday = birthday;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSnumber() {
return snumber;
}
public void setSnumber(String snumber) {
this.snumber = snumber;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", snumber=" + snumber + ", sname=" + sname + ", age=" + age + ", gender="
+ gender + ", birthday=" + birthday + "]";
}
}
测试添加数据
需要写sql,sql不是写在代码中,而是写在映射配置文件中
在resoruces资源包中创建mapper包,在mapper包中创建StudentMapper.xml
创建映射配置文件,在官网找模板
<?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">
<!--
映射配置文件就是写sql的地方
namespace: 给映射配置文件起个名字,可以帮助我们找到sql -->
<mapper namespace="studentMapper">
<!-- sql语句:通过标签来选择要进行的操作
id属性:唯一定位一句sql的 所以名称空间+sql的id可以找到一句sql -->
<insert id="add">
INSERT INTO student(snumber,sname,age,gender,birthday)
VALUES('20180203','黄晓明',23,1,'2022-06-07')
</insert>
</mapper>
在总配置文件中引入
<!-- 映射配置文件:之前注释掉了,现在打开并引入我们创建的映射xml文件 -->
<mappers>
<mapper resource="mapper/studentMapper.xml"/>
</mappers>
测试代码
@Test
public void testInsert() {
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(MyBatisTest.class.getResourceAsStream("/mybatis-config.xml"));
SqlSession openSession = sqlSessionFactory.openSession();
System.out.println(openSession);
int i = openSession.insert("studentMapper.add");
System.out.println("添加"+i);
//已经设置了非自动提交,所以执行完dml语句需要提交一下
openSession.commit();
}
添加时动态传入参数
<?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">
<!--
映射配置文件就是写sql的地方
namespace: 给映射配置文件起个名字,可以帮助我们找到sql -->
<mapper namespace="studentMapper">
<!-- sql语句:通过标签来选择要进行的操作
id属性:唯一定位一句sql的 所以名称空间+sql的id可以找到一句sql -->
<insert id="add">
INSERT INTO student(snumber,sname,age,gender,birthday)
VALUES(#{snumber},#{sname},#{age},#{gender},#{birthday})
</insert>
</mapper>
测试代码
@Test
public void testInsert1() {
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(MyBatisTest.class.getResourceAsStream("/mybatis-config.xml"));
SqlSession openSession = sqlSessionFactory.openSession();
System.out.println(openSession);
//创建要添加的学生对象
Student student = new Student(null, "20180204", "姜昆", 50, 0, new Date());
//将要拼接进sql的参数传入
int i = openSession.insert("studentMapper.add",student);
System.out.println("添加"+i);
//已经设置了非自动提交,所以执行完dml语句需要提交一下
openSession.commit();
}
完成增删改查
<?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">
<!--
映射配置文件就是写sql的地方
namespace: 给映射配置文件起个名字,可以帮助我们找到sql -->
<mapper namespace="studentMapper">
<!-- sql语句:通过标签来选择要进行的操作
id属性:唯一定位一句sql的 所以名称空间+sql的id可以找到一句sql -->
<insert id="add">
INSERT INTO student(snumber,sname,age,gender,birthday)
VALUES(#{snumber},#{sname},#{age},#{gender},#{birthday})
</insert>
<!-- 修改 -->
<update id="update">
UPDATE student SET snumber=#{snumber},sname=#{sname},age=#{age},gender=#{gender},birthday=#{birthday} WHERE sid=#{sid}
</update>
<!-- 删除 -->
<delete id="delete">
DELETE FROM student WHERE sid=#{sid}
</delete>
<!-- 查询所有 -->
<select id="findAll" resultType="com.hxj.domain.Student">
SELECT sid,snumber,sname,age,gender,birthday FROM student
</select>
<!-- 查询单个 -->
<select id="findById" resultType="com.hxj.domain.Student">
SELECT sid,snumber,sname,age,gender,birthday FROM student WHERE sid=#{sid}
</select>
</mapper>
测试
public class MybatisCrudTest {
SqlSession openSession;
@Before
public void before(){
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(MyBatisTest.class.getResourceAsStream("/mybatis-config.xml"));
openSession = sqlSessionFactory.openSession();
}
@Test
public void testInsert() {
Student student = new Student(null, "20180205", "马三", 20, 0, new Date());
int i = openSession.insert("studentMapper.add",student);
System.out.println("添加"+i);
}
@Test
public void testUpdate() {
Student student = new Student(10, "20180205", "马十三", 30, 0, new Date());
int i = openSession.insert("studentMapper.update",student);
System.out.println("修改"+i);
}
@Test
public void testDelete() {
int i = openSession.delete("studentMapper.delete",2);
System.out.println("删除"+i);
}
@Test
public void testfindOne() {
Student stu = openSession.selectOne("studentMapper.findById",10);
System.out.println("id为10的学生是:"+stu);
}
@Test
public void testfindAll() {
List<Student> stuList = openSession.selectList("studentMapper.findAll");
for (Student student : stuList) {
System.out.println(student);
}
}
@After
public void after(){
openSession.commit();
}
}
5.Mapper开发方式
5.1 步骤
前提:准备好jdbc.properties 总配置文件mybatis-config.xml 表对应的实体类
1.编写接口
2.在XML中编写SQL
3.编写实现类
4.单元测试
5.2 完整实现过程
5.2.1 准备表和测试数据
5.2.2 创建实体类
包:com.hxj.domain
package com.hxj.domain;
import java.util.Date;
public class Student {
private Integer sid;
private String snumber;
private String sname;
private Integer age;
private Integer gender;
private Date birthday;
public Student() {
super();
}
public Student(Integer sid, String snumber, String sname, Integer age, Integer gender, Date birthday) {
super();
this.sid = sid;
this.snumber = snumber;
this.sname = sname;
this.age = age;
this.gender = gender;
this.birthday = birthday;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSnumber() {
return snumber;
}
public void setSnumber(String snumber) {
this.snumber = snumber;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", snumber=" + snumber + ", sname=" + sname + ", age=" + age + ", gender="
+ gender + ", birthday=" + birthday + "]";
}
}
5.2.3 创建dao层接口
包:com.hxj.mapper
package com.hxj.dao;
import java.util.List;
import com.hxj.domain.Student;
public interface StudentMapper {
int add(Student Student);
int update(Student Student);
int delete(int id);
Student findById(int id);
List<Student> findAll();
}
5.2.4 创建mybatis总配置文件
创建resources源码包
在resources中创建jdbc.properties文件
在resources中创建mybatis-config.xml文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test1
username=root
password=root
<?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>
<!-- 引入属性文件 -->
<properties resource="jdbc.properties"></properties>
<!-- 配置环境:连接相关的信息 -->
<environments default="development">
<!-- 一个environment标签中配置一组连接信息 id是这组连接信息的唯一标识 -->
<environment id="development">
<!-- 使用事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据源:连接池 -->
<dataSource type="POOLED">
<!-- 数据库连接信息及数据源的配置 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>
5.2.5 创建映射配置文件
resources中创建com.hxj.mapper包,在里面创建StudentMapper.xml
注意:包名称和StudentMapper接口相同,文件名也和StudentMapper接口相同
StudentMapper.xml的namespace必须和接口的全限定名相同
接口中的sql语句的id和StudentMapper接口中的一一对应,必须和方法名相同
<?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">
<!--
映射配置文件就是写sql的地方
namespace: 给映射配置文件起个名字,可以帮助我们找到sql -->
<mapper namespace="com.hxj.dao.StudentMapper">
<!-- sql语句:通过标签来选择要进行的操作
id属性:唯一定位一句sql的 所以名称空间+sql的id可以找到一句sql -->
<insert id="add">
INSERT INTO student(snumber,sname,age,gender,birthday)
VALUES(#{snumber},#{sname},#{age},#{gender},#{birthday})
</insert>
<!-- 修改 -->
<update id="update">
UPDATE student SET snumber=#{snumber},sname=#{sname},age=#{age},gender=#{gender},birthday=#{birthday} WHERE sid=#{sid}
</update>
<!-- 删除 -->
<delete id="delete">
DELETE FROM student WHERE sid=#{sid}
</delete>
<!-- 查询所有 -->
<select id="findAll" resultType="com.hxj.domain.Student">
SELECT sid,snumber,sname,age,gender,birthday FROM student
</select>
<!-- 查询单个 -->
<select id="findById" resultType="com.hxj.domain.Student">
SELECT sid,snumber,sname,age,gender,birthday FROM student WHERE sid=#{sid}
</select>
</mapper>
5.2.6 引入映射配置文件
总配置文件中引入接口所在的包即可
<mappers>
<package name="com.hxj.dao"/>
</mappers>
5.2.7 Mybatis工具类的封装和CURD重构
public class MybatisUtil {
static SqlSessionFactory factory;
static{
try {
//加载主配置文件:1.加载属性文件 2.配置连接池 3.加载sql文件【写sql语句】
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//创建工厂对象
factory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
//通过工厂对象获取sql会话
return factory.openSession();
}
}
5.2.8 测试StudentMapper接口
/**
* 测试Mapper接口
* @author hxj12
*/
public class StudentMapperTest {
@Test
public void testMapper(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
System.out.println(mapper);
Student student = new Student(null, "20180206", "王五", 20, 0, new Date());
int add = mapper.add(student);
System.out.println("添加:"+add);
sqlSession.commit();
}
}
5.2.9 创建StudentService服务层
package com.hxj.service;
public class StudentService{
SqlSession sqlSession;
StudentMapper studentMapper;
public StudentService(){
sqlSession = MybatisUtil.getSqlSession();
//获取接口实现类对象 通过代理模式获取的动态生成的代理对象
studentMapper = sqlSession.getMapper(StudentMapper.class);
}
public boolean add(Student student) {
if(student==null){
return false;
}
if(student.getSnumber() == null || student.getSnumber().trim().equals("")){
return false;
}
int i = 0;
try {
i = studentMapper.add(student);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally{
sqlSession.close();
}
return i>0;
}
public boolean update(Student student) {
if(student==null){
return false;
}
if(student.getSid()==null){
return false;
}
if(student.getSnumber() == null || student.getSnumber().trim().equals("")){
return false;
}
int i = 0;
try {
i = studentMapper.update(student);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally{
sqlSession.close();
}
return i>0;
}
public boolean delete(int id) {
if(id<1){
return false;
}
int i = 0;
try {
i = studentMapper.delete(id);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally{
sqlSession.close();
}
return i>0;
}
public Student findById(int id) {
if(id<1){
return null;
}
return studentMapper.findById(id);
}
public List<Student> findAll() {
return studentMapper.findAll();
}
}
5.2.10 测试service层
package com.hxj.test;
/**
* 测试Service
* @author hxj12
*/
public class StudentServiceTest {
StudentService service;
@Before
public void before(){
service = new StudentService();
}
@Test
public void testAdd(){
Student student = new Student(null, "20180207", "小燕子", 20, 1, new Date());
boolean add = service.add(student);
System.out.println(add?"成功":"失败");
}
@Test
public void testUpdate(){
Student student = new Student(16, "20180207", "紫薇", 20, 1, new Date());
boolean update = service.update(student);
System.out.println(update?"成功":"失败");
}
@Test
public void testDelete(){
boolean delete = service.delete(10);
System.out.println(delete?"成功":"失败");
}
@Test
public void testfindOne(){
Student stu = service.findById(16);
System.out.println(stu);
}
@Test
public void testfindAll(){
List<Student> stuList = service.findAll();
for (Student student : stuList) {
System.out.println(student);
}
}
}
6.Mybatis使用细节
1.Mybatis中使用别名
2.添加时返回自增长主键
3.日志:
配置日志的好处:帮助排错
使用步骤:
1.导包3个
2.在类路径新建一个log4j.properties
3.将日志配置代码CV过来 日志配置文件内容在下面,要改成你的包名
6.1 配置别名
总配置文件:
映射配置文件中使用:
6.2 新增返回自增主键
6.3 日志
导入log4j的jar包
日志配置文件 log4j.properties:
#5.控制台输出+自定义布局
log4j.rootLogger=DEBUG,my
#指定输出器
log4j.appender.my=org.apache.log4j.ConsoleAppender
#指定布局器(自定义布局)
#指定布局为自定义布局
log4j.appender.my.layout=org.apache.log4j.PatternLayout
#指定在自定义布局的格式,%d -- 表示当前系统时间,%t -- 执行该业务的线程名称,%p -- 日记器的级别,-5 -- 5表示输出字符的个数,符号表示右对齐
#%c -- 表示指定业务所在的类的完全限定名(包名.类名),%m -- 输出额外信息,%n -- 表示换行
log4j.appender.my.layout.ConversionPattern=%-5p - %m%n
#设置package(可以是自定义的包也可以是api的包)输出级别,#trace - 可以看见sql查询的信息
log4j.logger.com.hxj=trace
6.4 #与$区别
在mybatis的SQL文件中可以使用#或$设置数据
能用#号就用#号
1.用#的时候会将#转成占位符? ,可以预防SQL注入
2.如果用$符号是用字符串拼接的方式
什么情况下只能用$号
要设置的参数是数据库对象【表名,字段名】
order by 字段
group by 字段
总结
mybatis:
ORM : 对象关系映射
持久层框架 : 封装了jdbc 脚手架 支撑 约束
配置文件
总配置文件:
1、引入属性集配置文件
2、配置别名
3、配置环境 连接信息
4、引入映射配置文件
映射配置文件:
namespace: 作用是找到一组映射
sql :
id属性必须写 而且不允许重复
dml insert update delete 不用配置返回值类型
dql select 一定要写返回值类型 resultType="实体类的全限定名" 如果配了别名 直接使用实体类名
参数:
单个 #{随意起名}
多个 #{属性名称} 本质要看获取的属性的get方法的bean属性名
API
会话工厂 SqlSessionFactory
会话 SqlSession
insert("namespace.sqlid",实体对象/参数本身)
update
delete
selectOne
selectList
默认开启了非自动提交
dml操作完成之后必须commit()
使用细节
1、别名配置
2、自增主键
3、日志
引入3个jar包
创建log4j.properties配置文件
4、#和$的区别
# ------》 ?
$ ------》 字符串拼接 【有sql注入的风险】 【表名和字段名作为参数传入必须用】