传统JDBC编程存在的弊端
1)工作量大,操作数据库至少要5步
1.加载JDBC驱动Class.forName("com.mysql.jdbc.Driver");
2.与数据库建立连接DriverManager.getConnection(url,uname,pwd);
3.获取操作对象,发送sql语句得到返回结果
4.处理返回结果
5.释放资源
2)业务代码和技术代码耦合
3)连接资源手动关闭,带来隐患
框架概述
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。它使用ORM的思想实现了结果集的封装。
MyBatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。
三层框架:
表现层: 用于展示数据
业务层: 处理业务需求
持久层:和数据库进行交互
ORM思想
ORM(Object Relational Mapping)模型就是数据库表与简单Java对象(POJO)的映射模型,它主要解决数据库数据和Java实体对象的相互映射。
ORM工具的唯一作用就是:把对持久化对象的保存、修改、删除等操作,转换成对数据库的操作。由此,程序员可以以面向对象的方式操作持久化对象,而ORM框架则负责将相关操作转换成对应的SQL(结构化查询语言)操作。
ORM带来的好处:
1)更加贴合面向对象的编程语义,Java程序员喜欢的姿势;
2)技术和业务解耦
3)自动释放数据库连接资源
MyBatis入门
概述
mybatis是一个持久层框架,用java编写的。它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程。它使用了ORM思想实现了结果集的封装。
MyBatis需要提供的映射文件包括三个部分:
- SQL
- 映射规则
- POJO
MyBatis的核心组件:
SqlSession
SqlSession是一个接口类,真正干活的是Executor接口。SqlSession相当JDBC中的Connection接口对象,需要每次使用完成后都进行关闭,正确的做法是将它关闭的代码写入finally语句中。如果不及时关闭资源,数据库的链接资源很快就会被耗尽。
SqlSession的作用有两个:
- 获取映射器,让映射器通过命名空间和方法名称找到对应的SQL,发送你给数据库返回结果。
- 直接通过命名信息指向SQL返回结果
映射器
映射器是由Java 接口和XML 文件(或注解)共同组成的,它的作用如下。
- 定义参数类型。
- 描述缓存。
- 描述SQL 语句。
- 定义查询结果和POJO 的映射关系。
mybatis的环境搭建-使用xml文件
第一步:创建maven工程并导入坐标,在pom.xml中添加Mybatis框架所需要依赖的坐标(jar包)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lwl</groupId>
<artifactId>MyBatis</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式-->
<packaging>jar</packaging>
<!--定位相关依赖-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
</project>
第二步:创建实体类和dao的接口。
public interface IUserDao {
/**
* 查询所有操作
* @return
*/
List<User> findAll();
}
第三步:创建Mybatis的主配置文件 SqlMapConifg.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">
<!--Mybatis主配置文件-->
<configuration>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/user"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</dataSource>
</environment>
</environments>
<!--使用resource指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/lwl/dao/IUserDao.xml"></mapper>
</mappers>
</configuration>
第四步:创建映射配置文件 IUserDao.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">
<mapper namespace="com.lwl.dao.IUserDao">
<!--select为SQL中的关键词,id对应类中查询的函数名称,resultType中填写被封装的类的全类名-->
<select id="findAll" resultType="com.lwl.domain.User">
</select>
</mapper>
环境搭建的注意事项:
1. 创建IUserDao.xml 和 IUserDao.java时名称是为了和之前的知识保持一致。
在Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper
所以在其他项目中:IUserDao 和 IUserMapper是一样的
2. 在idea中创建目录的时候,它和包是不一样的
包在创建时:com.itheima.dao它是三级结构
目录在创建时:com.itheima.dao是一级目录
3. mybatis的映射配置文件位置必须和dao接口的包结构相同
4. 映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
5. 映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名
测试
进行查询操作,在IUserDao.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">
<mapper namespace="com.lwl.dao.IUserDao"> <!-- 填写需要映射的Dao -->
<!--配置查询所有-->
<!--id与接口关联,resultType与要封装的实体对应-->
<select id="findAll" resultType="com.lwl.domain.User">
select * from user;
</select>
</mapper>
用一个select 元素定义了一个查询SQL , id 为findAll,和我们接口方法是一致的,, 而resultType 则定义我们需要返回的数据类型的权限定类名。
新建测试类进行查询:
第一步:读取配置文件
第二步:创建SqlSessionFactory工厂
第三步:创建SqlSession
第四步:创建Dao接口的代理对象
第五步:执行dao中的方法
第六步:释放资源
package com.lwl.test;
import com.lwl.dao.IUserDao;
import com.lwl.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import javax.annotation.Resources;
import java.io.InputStream;
import java.util.List;
/**
* @author liwenlong
* @data 2020/5/3
*/
public class MybatisTest {
@Test
public void test() throws Exception{
//1.读取配置文件
InputStream is = Resources.class.getResourceAsStream("/SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
//3.使用工厂生产SqlSessionFactory对象
SqlSession session = factory.openSession();
//4.使用SqlSession的实例创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//5.使用动态代理执行方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6.释放资源
session.close();
is.close();
}
}
输出结果:
mybatis的环境搭建-使用注解
重新使用注解的形式再进行一遍,这次不再使用 IUserDao.xml。
方法:
把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句
同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。
步骤:
第一步和使用xml方式相同:创建maven工程并导入坐标,在pom.xml中添加Mybatis框架所需要依赖的坐标(jar包)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lwl</groupId>
<artifactId>MyBatis</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式-->
<packaging>jar</packaging>
<!--定位相关依赖-->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
</project>
第二步:创建实体类和dao的接口,和之前的区别是增加注解。
public interface IUserDao {
/**
* 查询所有操作
* @return
*/
//加上注解
@Select("select * from user")
List<User> findAll();
}
第三步:创建Mybatis的主配置文件 SqlMapConifg.xml (数据库连接和指定映射配置文件的位置[目的是为了不用自己实现数据库查询方法])
和xml配置方式的区别是在指定映射配置文件的位置的时候,应该使用class属性指定被注解的dao权限定类名。
<?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>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql环境-->
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<dataSource type="POOLED">
<!--配置连接数据库的四个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/user"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,应该使用class属性来指定备注接的dao权限定类名-->
<mappers>
<mapper class="com.lwl.dao.IUserDao"></mapper>
</mappers>
</configuration>
测试
还是使用之前写的测试类:
package com.lwl.test;
import com.lwl.dao.IUserDao;
import com.lwl.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import javax.annotation.Resources;
import java.io.InputStream;
import java.util.List;
/**
* @author liwenlong
* @data 2020/5/3
*/
public class MybatisTest {
@Test
public void test() throws Exception{
//1.读取配置文件
InputStream is = Resources.class.getResourceAsStream("/SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
//3.使用工厂生产SqlSessionFactory对象
SqlSession session = factory.openSession();
//4.使用SqlSession的实例创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//5.使用动态代理执行方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//6.释放资源
session.close();
is.close();
}
}
输出的结果和xml文件配置方式输出的结果一样。
不管使用XML还是注解配置,我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。但是Mybatis它是支持写dao实现类的。
代码分析
测试类
1. 读取配置文件
InputStream is = Resources.class.getResourceAsStream("/SqlMapConfig.xml");
路径分为绝对路径和相对路径,但是使用绝对路径的话,在本地的文件位置就不能发生变动,否则会出错。使用相对路径的话,当项目被部署之后,src等目录就会消失。因此在实际开发项目中,两种都不会使用,而是使用以下两种:
第一个:使用类加载器,他只能读取类路径的配置文件
第二个:使用ServletContext对象的getRealPath()
2.创建SqlSessionFactory工厂,使用工厂创建对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
创建工厂MyBatis使用了 构建者模式 , 把对象的创建细节隐藏,使用者直接调用方法就可以得到对象。
3.使用SqlSession的实例创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
mybatis在使用代理dao的方式实现增删改查时只做两件事,
第一:创建代理对象
第二:在代理对象中调用selectList
主配置文件
主要包含数据库配置信息:
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/user"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
映射配置信息,其中包含执行SQL的语句,可以获取PreparedStatement,并且需要包括全类名。
<?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="com.lwl.dao.IUserDao"> <!-- 填写需要映射的Dao -->
<!--配置查询所有-->
<!--id与接口关联,resultType与要封装的实体对应-->
<select id="findAll" resultType="com.lwl.domain.User">
select * from user;
</select>
</mapper>
程序中读取配置文件:用到的技术就是解析xml的技术,此处使用dom4j解析xml技术。
此部分相当于在JDBC中根据配置文件的信息创建Connection对象,注册驱动,获取PreparedStatement连接对象。而PreparedStatement对象需要SQL语句,SQL语句就从解析的XML文件中获取。
接下来该执行查询操作,查询出结果集,然后对结果集进行遍历封装成对象。
而在MyBatis中药封装成对象就要先得到这个对象,就是在xml文件中的resultType来指定将要封装成的对象的位置。由于创建的类中属性名称和表的列名是一直的,我们可以将表的列名看成是实体类的属性名称,通过反射的方式来根据名称获取每个属性,并赋值到这个对象中。
如果想要像JDBC那样执行,就需要两个信息,一个是连接信息,另外一个映射信息。连接信息就是连接数据库需要的四个基本要素。映射信息需要包括执行的SQL语句和被封装类的权限定类名。因此在MyBatis中在解析xml的过程中,将SQL语句和被封装类的权限定类名封装成一个对象进行获取,可以使用一个Mapper类进行封装。
自定义mybatis开发流程图
生命周期
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 是利用XML 或者Java 编码获得资源来构建SqlSessionFactory的,通过它可以构建多个SessionFactory 。它的作用就是一个构建器,一旦我们构建了SqlSessionFactory ,它的作用就已经完结,失去了存在的意义,这时我们就应该毫不犹豫的废弃它,将它回收。所以它的生命周期只存在于方法的局部,它的作用就是生成SqlSessionFactory 对象。
SqlSessionFactory
SqlSessionFactory 的作用是创建SqlSession ,而SqlSession 就是一个会话,相当于JDBC中的Connection 对象。每次应用程序需要访问数据库,我们就要通过SqlSessionFactory 创建SqlSession ,所以SqlSessionFactory 应该在MyBatis应用的整个生命周期中。
SqlSession
它的生命周期应该是在请求数据库处理事务的过程中。它是一个线程不安全的对象,在涉及多线程的时候我们需要特别的当心,操作数据库需要注意其隔离级别,数据库锁等高级特性。此外,每次创建的SqlSession 都必须及时关闭它,它长期存在就会使数据库连接池的活动资源减少,对系统性能的影响很大。