原始jdbc存在的问题
-
存在问题
- ①频繁地创建和销毁Connection对象
- ②driverClass、url、username、password、sql语句都存在字符串硬编码
- ③手动设置输入参数
- 输入映射:将输入的值映射到占位符
- ④手动处理结果集
- 输出映射:将结果集映射到javabean
- ⑤手动释放资源
-
解决方案(使用mybatis)
- ①引入连接池
- ②使用配置文件
- ③使用反射+缺省操作
- ④使用反射+缺省操作
- ⑤缺省操作
mybatis基本概念
- 概述
- mybatis是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接 等繁杂的过程
- 结构
mybatis入门程序
- 官网
- 开发步骤
- ①引入mybatis相关依赖
- ②创建mybatis的核心配置文件
- 配置数据库环境(事务管理器、数据源)
- 加载mapper映射文件
- ③创建mapper映射配置文件
- 配置MappedStatement
- ④定义dao接口及其实现子类
- 1,读取mybatis的核心配置文件
- 2,创建SqlSessionFactory对象
- 3,获取SqlSession对象
- 4,操作对应的MappedStatement对象
- 5,释放资源
- ⑤代码测试
- ①引入mybatis相关依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
②创建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">
<configuration>
<environments default="development">
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--加载映射配置文件-->
<mappers>
<mapper resource="mapper01.xml"/>
</mappers>
</configuration>
③创建mapper映射配置文件
<?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="test">
<!--
配置MappedStatement
id:statement唯一标识
parameterType: 输入映射的类型
resultType: 输出映射的类型
-->
<select id="selectExamById" parameterType="int" resultType="com.atguigu.pojo.Exam">
select * from tb_exam where id = #{id}
</select>
</mapper>
④定义dao接口及其实现子类
public class ExamDaoImpl implements ExamDao {
@Override
public Exam selectExamById(Integer id) throws Exception {
//1,读取mybatis核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//2,获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3,打开会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4,执行MappedStatement
Exam exam = sqlSession.selectOne("selectExamById", id);
//5,关闭会话
sqlSession .close();
return exam;
}
}
⑤代码测试
public class ExamDaoTest {
@Test
public void selectExamById() throws Exception {
ExamDao examDao = new ExamDaoImpl();
Exam exam = examDao.selectExamById(1);
System.out.println("exam = " + exam);
}
}
mapper文件说明
- 映射文件
05-添加用户(掌握)
添加用户
代码实现
<!--添加用户-->
<insert id="addExam" parameterType="com.atguigu.pojo.Exam">
insert into tb_exam
values (null, #{name}, #{math}, #{english}, #{chinese})
</insert>
@Override
public int addExam(Exam inputExam) throws Exception {
//1,读取mybatis核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//2,获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3,打开会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4,执行MappedStatement
int addExam = sqlSession.insert("addExam", inputExam);
//5,提交事务
sqlSession.commit();
//6,关闭会话
sqlSession.close();
return addExam;
}
注意事项
- mybatis的事务是手动提交
删除用户
代码实现
<delete id="deleteExamById" parameterType="int">
delete
from tb_exam
where id = #{id}
</delete>
@Override
public int deleteExamById(Integer id) throws Exception {
//1,读取mybatis核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//2,获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3,打开会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4,执行MappedStatement
int deleteExamById = sqlSession.insert("deleteExamById", id);
//5,提交事务
sqlSession.commit();
//6,关闭会话
sqlSession.close();
return deleteExamById;
}
修改用户
代码实现
<update id="updateExamById" parameterType="com.atguigu.pojo.Exam">
update tb_exam
set name = #{name},
math = #{math},
english = #{english},
chinese = #{chinese}
where id = #{id}
</update>
@Override
public int updateExamById(Exam inputExam) throws Exception {
//1,读取mybatis核心配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//2,获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3,打开会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4,执行MappedStatement
int updateExamById = sqlSession.insert("updateExamById", inputExam);
//5,提交事务
sqlSession.commit();
//6,关闭会话
sqlSession.close();
return updateExamById;
}
mybatis相关API
Resources类介绍
- 概述
用于读取资源的工具类
SqlSessionFactoryBuilder类介绍
- 概述
这个类可以被实例化、使用和丢弃,一旦创建类SqlFactory,就不在需要它
但最好不要一直保留它
最好使用匿名创建
SqlSessionFactory接口介绍
概述
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。
SqlSession接口介绍
- 概述
- 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 一次数据库操作就应该是一个新的SqlSession对象
mybatis传统dao开发
- 概述
- mybatis开发有两种方式:1,传统dao开发;2,mapper接口代理开发
- 传统dao开发 : dao接口 + dao接口实现子类 + mapper映射文件
- 代码实现
public class ExamDaoImpl implements ExamDao {
private SqlSessionFactory sqlSessionFactory ;
//后续和spring框架整合之后,就可以保证sqlSessionFactory对象是一个单例对象
public ExamDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public Exam selectExamById(Integer id) throws Exception {
//3,打开会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4,执行MappedStatement
Exam exam = sqlSession.selectOne("selectExamById", id);
//5,关闭会话
sqlSession .close();
return exam;
}
@Override
public int addExam(Exam inputExam) throws Exception {
//3,打开会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4,执行MappedStatement
int addExam = sqlSession.insert("addExam", inputExam);
//5,提交事务
sqlSession.commit();
//6,关闭会话
sqlSession.close();
return addExam;
}
@Override
public int deleteExamById(Integer id) throws Exception {
//3,打开会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4,执行MappedStatement
int deleteExamById = sqlSession.insert("deleteExamById", id);
//5,提交事务
sqlSession.commit();
//6,关闭会话
sqlSession.close();
return deleteExamById;
}
@Override
public int updateExamById(Exam inputExam) throws Exception {
//3,打开会话
SqlSession sqlSession = sqlSessionFactory.openSession();
//4,执行MappedStatement
int updateExamById = sqlSession.insert("updateExamById", inputExam);
//5,提交事务
sqlSession.commit();
//6,关闭会话
sqlSession.close();
return updateExamById;
}
}
- 存在问题
- ①mapper映射文件和dao实现子类的耦合度过高
- ②开发中会有大量的dao实现子类,难以维护
mybatis接口代理开发
- 概述
- dao接口 + mapper映射文件
- 开发步骤
- ①mapper映射文件要和接口放在同一个目录中
- ②mapper映射文件要和接口名称一致
- ③mapper映射文件的namespace和接口的全限定类名一致
- ④MappedStatement的id和接口的方法名称一致
- ⑤MappedStatement的parameterType和接口的方法的形参类型一致
- ⑥MappedStatement的resultType和接口的方法的返回值类型一致
- ⑦代码测试
- 代码实现
<?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.atguigu.dao.ExamDao">
<select id="selectExamById" parameterType="int" resultType="com.atguigu.pojo.Exam">
select *
from tb_exam
where id = #{id}
</select>
<!--添加exam-->
<insert id="addExam" parameterType="com.atguigu.pojo.Exam">
insert into tb_exam
values (null, #{name}, #{math}, #{english}, #{chinese})
</insert>
<!--删除exam-->
<delete id="deleteExamById" parameterType="int">
delete
from tb_exam
where id = #{id}
</delete>
<!--修改exam-->
<update id="updateExamById" parameterType="com.atguigu.pojo.Exam">
update tb_exam
set name = #{name},
math = #{math},
english = #{english},
chinese = #{chinese}
where id = #{id}
</update>
</mapper>
public class ExamDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("SqlMapConfig.xml"));
}
@Test
public void selectExamById() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
ExamDao examDao = sqlSession.getMapper(ExamDao.class);
Exam exam = examDao.selectExamById(2);
System.out.println("exam = " + exam);
sqlSession.close();
}
@Test
public void addExam() {
}
@Test
public void deleteExamById() {
}
@Test
public void updateExamById() {
}
}
SqlMapperConfig配置文件
- 概述:
①properties:用于mybatis中声明变量
②setting:缓存、懒加载、日志实现等等
③typeAlias:设置别名
④plugins:设置插件
⑤environments:事务管理器、数据源
⑥mappers:加载mapper映射文件
mybatis内置连接池
- 概述
- 在Mybatis中也有连接池技术,但是它采用的是自己的连接池技术。在 Mybatis 的 SqlMapConfig.xml配置文件中,通过来实现Mybatis 中连 接池的配置。
- 分类
- JndiDataSourceFactory
- PooledDataSource
- UnpooledDataSource
enivronments标签
- 组成
- transactionManager
- JDBC : 有事务管理
- MANAGED : 无事务管理
- dataSource
- POOLED
- UNPOOLED
- transactionManager
- 代码实现
<environments default="development2">
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="development2">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
properties标签
- 概述;
用于声明变量并复用
代码实现
<properties>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<environments default="development">
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="${driverClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<properties resource="jdbc.properties"></properties>
<!--整合spring之后弃用-->
<environments default="development">
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="${driverClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
settings标签之log4j
- 概述
- 在日常开发过程中,排查问题时难免需要输出MyBatis真正执行的SQL语句、参数、结果等 信息,我们就可用借助log4j的概念来实现执行信息的输出
- 开发步骤
- ①引入log4j相关依赖
- ②引入log4j.xml配置文件
- ③配置mybatis的日志实现
- ①引入log4j相关依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
②引入log4j.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n"/>
</layout>
</appender>
<logger name="java.sql">
<level value="debug"/>
</logger>
<logger name="org.apache.ibatis">
<level value="info"/>
</logger>
<!--开启全局日志打印-->
<root>
<level value="debug"/>
<appender-ref ref="STDOUT"/>
</root>
</log4j:configuration>
③配置mybatis的日志实现
<properties resource="jdbc.properties"></properties>
settings标签之mapUnderscoreToCamelCase
- 概述
- 数据库中,表的字段名称为:a_column;java中,类的属性名称为:aColumn。
- 如果设置mapUnderscoreToCamelCase=true,a_column字段就会自动输出映射到aColumn。
- 代码实现
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
typeAliasees
- 概述
- 设置java类型的别名
- 代码实现
<typeAliases>
<typeAlias type="com.atguigu.pojo.User" alias="user" ></typeAlias>
<typeAlias type="com.atguigu.pojo.Exam" alias="exam" ></typeAlias>
</typeAliases>
<typeAliases>
<package name="com.atguigu.pojo"/>
</typeAliases>
mappers标签
- 概述
- 加载映射配置文件
- 代码实现
<!--加载映射配置文件-->
<mappers>
<!--传统dao开发、接口代理开发-->
<mapper resource="com/atguigu/dao/ExamDao.xml"/>
<mapper resource="com/atguigu/dao/UserDao.xml"/>
</mappers>
<!--加载映射配置文件-->
<mappers>
<!--接口代理开发-->
<mapper class="com.atguigu.dao.UserDao"></mapper>
<mapper class="com.atguigu.dao.ExamDao"></mapper>
</mappers>
<mappers>
<!--接口代理开发-->
<package name="com.atguigu.dao"/>
</mappers>
核心配置文件之plugins插件
- 概述
- MyBatis可以使用第三方的插件对功能进行扩展,分页助手PageHelper是将分页的复杂操作 进行封装,使用简单的方式即可获得分页的相关数据
- 开发步骤
- ①引入pagehelper相关依赖
- ②在SqlMapConfig.xml中配置pagehelper
- ③定义service接口及其实现子类
- 编写分页代码
- ④定义dao接口及其实现子类
- ⑤代码测试
- ①引入pagehelper相关依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
②在SqlMapConfig.xml中配置pagehelper
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="reasonable" value="true"/>
</plugin>
</plugins>
③定义service接口及其实现子类
@Override
public PageInfo<Exam> selectExamListByPage(Integer currentPage, Integer pageSize) throws Exception {
//开启分页查询
PageHelper.startPage(currentPage, pageSize);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("SqlMapConfig.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
ExamDao examDao = sqlSession.getMapper(ExamDao.class);
List<Exam> examList = examDao.selectExamList();
sqlSession.close();
return new PageInfo<>(examList);
}
**- ④定义dao接口及其实现子类
- ⑤代码测试**
@Test
public void selectExamListByPage() throws Exception {
ExamService examService = new ExamServiceImpl();
PageInfo<Exam> pageInfo = examService.selectExamListByPage(2, 2);
System.out.println("pageInfo = " + pageInfo);
System.out.println("当前页数:"+pageInfo.getPageNum());
System.out.println("每页记录数:"+pageInfo.getPageSize());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("上一页的页码:"+pageInfo.getPrePage());
System.out.println("下一页的页码:"+pageInfo.getNextPage());
System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
System.out.println("总记录数:"+pageInfo.getTotal());
System.out.println("当前页数据:"+pageInfo.getList());
}