目录
学习目标
- Mybatis的介绍
- Mybatis的入门
a)使用jdbc操作数据库存在的问题 (回顾jdbc)
b)Mybatis的架构
c)Mybatis的入门程序 (重点) - Dao的开发方法
a)原始dao的开发方法 (了解)
b)接口的动态代理方式 (重点)
Jdbc介绍
- 案例: jdbc 做一个根据名称查询用户的操作
- 什么是jdbc?
Java Database Connectivity ,Java数据库连接 ,简称JDBC
Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法 - 什么是数据库驱动?
数据库提供商对JDBC接口的实现类,如mysql,oracle,sqlserver
//接口
public interface JdbcApi{
public void update();
}
//接口不能直接创建对象
JdbcApi api = new JdbcApi();
api.update();
//Myql厂商
public class MysqlJdbc implements JdbcApi{
public void update(){
//Mysql写了代码
}
}
MysqlJdbc api = new MysqlJdbc();
api.update();
//Oracle 厂商
public class OracleJdbc implements JdbcApi{
public void update(){
//OracleJdbc写了代码
}
}
OracleJdbc api = new OracleJdbc();
api.update();
Jdbc操作数据库原理图
- Jdbc如何操作Mysql?
- 加载数据库驱动
- 创建并获取数据库链接
- 创建jdbc statement对象
- 设置sql语句
- 设置sql语句中的参数(使用preparedStatement)
- 通过statement执行sql并获取结果
- 对sql执行结果进行解析处理
- 释放资源(resultSet、preparedstatement、connection)
Jdbc连接池原理图
- 频繁创建连接,销毁连接造成极大资源开销
- 连接池,即一个集合,可以管理连接对象
- 有什么特点?
- 可以初始化指定数量的连接
- 可以指定集合的最大元素个数
- 可以申请连接池的连接
- 不用的连接可以放回连接池
Jdbc代码分析总结
- jdbc 做一个根据名称查询用户的操作 搜索
- 初始化数据库
create database mybatis01;
use mybatis01;
drop table if exists `user`;
create table `user` (
`id` int(11) primary key auto_increment,
`username` varchar(32) ,
`birthday` date,
`sex` char(1) ,
`address` varchar(256)
)
insert into `user` values ('1', '王五', '2014-07-10', '2', null);
insert into `user` values ('10', '张三', '2014-08-11', '1', '北京市');
insert into `user` values ('16', '张小明', '2015-07-10', '1', '河南郑州');
insert into `user` values ('22', '陈小明', '2016-07-10', '2', '重庆');
insert into `user` values ('24', '张三丰', '2019-07-10', '1', '天津');
insert into `user` values ('25', '陈小明', '2019-07-10', '1', '杭州');
insert into `user` values ('26', '王五', '2018-07-10', null, null);
- 配置mysql驱动的依赖
- 创建maven工程,再创建web01模块
- 配置依赖
- pom.xml
<properties>
<mysql.version>5.1.32</mysql.version>
</properties>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
- 分析jdbc编写操作数据库代码的可改进地方
- 频繁创建、释放数据库连接 ,性能开销大,没有自带连接池
- Sql语句写在java代码中 ,硬编码
- sql语句的where条件与占位符赋值没有写判断逻辑
- 数据库记录需封装成pojo对象 User类 大量的编写 转换代码
public class TestJdbc {
@Test
public void test01() throws Exception {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis01?characterEncoding=utf-8", "root", "123456");
// 定义sql语句 ?表示占位符
String sql = "select * from user where username = ?";
// 获取预处理statement
preparedStatement = connection.prepareStatement(sql);
// 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
// 向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();//将sql发送给数据库
// 遍历查询结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("id") + " " + resultSet.getString("username"));
}
// 释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
Mybatis介绍
- Mybatis是什么?
MyBatis 本是apache的一个开源项目iBatis,是一个基于Java的持久层框架,用来写dao - Mybatis有什么特点?
- 最简单安装只要两个jar文件+配置几个sql映射文件,自带连接池
- sql写在xml里,便于统一管理和优化
- sql和代码的分离,提高了可维护性
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql
总结一句,重点在xml配置,很少写java代码。
Mybatis架构
- mybaits 是对jdbc的封装
- 重点在配置文件xml
- 架构图
- 核心 配置文件,放四大信息
ip,port,username,password - 映射文件
select
update
delete
insert - SqlSessionFactory
使用了工厂设计模式的生成session的对象
SqlSessionFactory是MyBatis的关键对象,它是个单个数据库映射关系经过编译后的内存镜像。
SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象类获得,而SqlSessionFactoryBuilder则可以从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例。
每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心.同时SqlSessionFactory也是线程安全的,SqlSessionFactory一旦被创建,应该在应用执行期间都存在,在应用运行期间不要重复创建多次
建议使用单例模式.SqlSessionFactory是创建SqlSession的工厂。
- SqlSession对象
支持增删改查,连接
SqlSession是MyBatis的关键对象,类似于JDBC中的Connection。
SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法,它的底层封装了JDBC连接,可以用SqlSession实例来直接执行被映射的SQL语句。每个线程都应该有它自己的SqlSession实例.SqlSession的实例不能被共享
同时SqlSession也是线程不安全的,绝对不能将SqlSeesion实例的引用放在一个类的静态字段甚至是实例字段中.也绝不能将SqlSession实例的引用放在任何类型的管理范围中
比如Servlet当中的HttpSession对象中.使用完SqlSeesion之后关闭Session很重要,应该确保使用finally块来关闭它.
由于JDBC的Connection对象本身不是线程安全的,而session中又只有一个connection,所以不是线程安全的
- 线程安全:
指多个线程在执行同一段代码的时候采用加锁机制,使每次的执行结果和单线程执行的结果都是一样的,不存在执行程序时出现意外结果。 - 线程不安全:
是指不提供加锁机制保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
Mybatis环境搭建
- mybatis下载
mybaits的代码由github.com管理
下载地址:https://github.com/mybatis/mybatis-3/releases - 导入jar包
测试相关的包: junit hamcrest
日志相关的包:log4j-1.2.16 slf4-api slf4j-log4j12
数据库相关jar包: mysql-connector
Mybatis先关jar包: mybaits-3.4.5
因为有了Maven所以直接去仓库找gav即可
pom.xml
<dependencies>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--日志包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
- 导入jar包log4j.properties
- 创建log4j.properties文件
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
Mybatis的操作步骤
- mybatis开发流程
- 创建SqlSessionFactoryBuilder对象
- 创建SqlSessionFactory对象
- 加载SqlMapConfig.xml配置文件
- 创建SqlSession对象
- 执行SqlSession对象执行删除
- 打印结果
- 释放资源
Mybatis的入门案例-Java代码编写
(1)先获取Session
(2)再进行增删改查操作
src\main\resources\ UserMapper.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">
<!-- map的约束文件-->
<!-- namespace 命名空间 防止id冲突找不到正确的id-->
<!-- id Mybatis是将sql语句写在xml文件中,以后session根据namesapce.id 合在一起获取sql-->
<!-- 在jdbc中,sql语句使用?作占位符,但在mybatis #{id}-->
<mapper namespace="com.vission.bean.User">
<delete id="deleteById" parameterType="int" >
delete from user where id = #{id}
</delete>
</mapper>
src\main\resources\ SqlMapConfig.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">
<!--dtd是对xml配置标签与属性的约束-->
<configuration>
<!-- environments环境 开发环境,生产环境-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc的事务管理 -->
<transactionManager type="JDBC"/>
<!-- dataSource Mybatis自带数据源连接池-->
<dataSource type="POOLED">
<!-- 四大信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis01?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 配置多个映射文件-->
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
src\test\java\com\vission\pack01\Test01Myabatis.java
public class Test01Myabatis {
@Test
public void test01(){
// 》1 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 》2 创建SqlSessionFactory对象
InputStream inputStream = Test01Myabatis.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(inputStream);//加载核心配置文件 参1 输入流
// 》3 加载SqlMapConfig.xml配置文件
// 》4 创建SqlSession对象
SqlSession sqlSession = sessionFactory.openSession();
// 》5 执行SqlSession对象执行删除
System.out.println(sqlSession);
//delete from user where id = ?
sqlSession.delete("com.vission.bean.User.deleteById",1);//执行删除 参1 是namespace.id 参2 参数值
// 》6 打印结果
// 》7 释放资源
sqlSession.commit();//mybatis的session不会自动提交,必须手动提交
sqlSession.close();
}
}
封装 MySessionUtil
(1)静态代码块
(2)getSession方法
src\main\java\com\vission\util\MySessionUtils.java
//Session工具类
public class MySessionUtils {
private static SqlSessionFactory sessionFactory;
//static 静态代码,在类加载的时候执行一次,且只执行一次
static{
// 》1 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 》2 创建SqlSessionFactory对象
InputStream inputStream = MySessionUtils.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml");
sessionFactory = sqlSessionFactoryBuilder.build(inputStream);//加载核心配置文件 参1 输入流
// 》3 加载SqlMapConfig.xml配置文件
}
public static SqlSession getSession() {
// 》4 创建SqlSession对象
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession;
}
}
- 测试类
src\test\java\com\vission\pack01\Test02Myabatis.java
public class Test02Myabatis {
@Test
public void test01(){
SqlSession sqlSession = MySessionUtils.getSession();
// 》5 执行SqlSession对象执行删除
System.out.println(sqlSession);
//delete from user where id = ?
sqlSession.delete("com.vission.bean.User.deleteById",24);//执行删除 参1 是namespace.id 参2 参数值
// 》6 打印结果
// 》7 释放资源
sqlSession.commit();//mybatis的session不会自动提交,必须手动提交
sqlSession.close();
}
}
getMapper 实现增删改查
动态代理-根据id查询
- 传统DAO需要接口,需要实现类
实现类需要sqlSession.selectOne(“getUserById”,id),传统dao的问题就是当xml中的id变动的时候,需要在实现类中去修改代码,当成很大的不方便。
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
User user =sqlSession.selectOne("getUserById",id);
sqlSession.close();
- 动态代理dao只要接口不再需要实现类
只要保证xml中的id和接口的方法名一致,所以只要改接口的方法名和xml中的id名字对应就可以了,大大增加了代码的灵活性。耦合度低
动态代理dao开发规则
- namespace必需是接口的全路径名
- 接口的方法名必需与映射文件的sql id一致
- 接口的输入参数必需与映射文件的parameterType类型一致
- 接口的返回类型必须与映射文件的resultType类型一致
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(30);
System.out.println(user);
sqlSession.close();
- 接口与类是实现关系
public interface MyDao{
public void save();
}
//编写实现类文件
public class MyDaoClass implements MyDao{
public void save(){
System.out.println("Hello");
//sql
//jdbc
}
}
- 动态代理生成实现类
src\main\java\com\vission\dao\UserDao.java
//com.vission.dao.UserDao.findById
public interface UserDao {
public User findById(int id);
}
src\main\resources\com\vission\dao\UserDao.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">
<!--
使用代理以后 当前的映射文件要与接口同名,并且放在同一个位置
使用代理以后 namespace要求写接口的全类名 包名+类名=全类名
UserDao-->
<mapper namespace="com.vission.dao.UserDao">
<!-- 使用代理以后id必须是方法名-->
<select id="findById" parameterType="int" resultType="com.wzx.bean.User">
select * from user where id =#{id};
</select>
</mapper>
src\test\java\com\vission\pack02\Test01Proxy.java
public class Test01Proxy {
@Test
public void test01(){
SqlSession sqlSession = MySessionUtils.getSession();
//select * from user where id =1;
UserDao dao = sqlSession.getMapper(UserDao.class);//内部就使用你编写接口来生成代理对象
//代理对象的方法内部 查询sql,并且执行jdbc代码
User user = dao.findById(1);
System.out.println(user);
//定义接口
sqlSession.close();
}
}
src\main\resources\SqlMapConfig.xml
<!-- 配置多个映射文件-->
<mappers>
<mapper resource="UserMapper.xml"/>
<mapper resource="com/vission/dao/UserDao.xml"/>
<--!>
</mappers>
根据用户名模糊查询
(1)定义接口中的方法
(2)语句
(3)配置
(4)通过session.getMapper()返回的对象不是实现类对象
@Test
public void test02(){
SqlSession sqlSession = MySessionUtils.getSession();
//返回的是实现接口的实现类对象
UserDao dao = sqlSession.getMapper(UserDao.class);
//搜索名字
List<User>list=dao.findByKeyWord("张%");
System.out.println(list);
//定义接口
sqlSession.close();
}
UserDao .java
//com.wzx.dao.UserDao.findById
public interface UserDao {
public User findById(int id);
//select * from user where username like '张%'
List<User> findByKeyWord(String s);
}
UserDao .xml
<select id="findByKeyWord" parameterType="string" resultType="com.wzx.bean.User">
select * from user where username like #{keyword}
</select>
删除数据
(1)定义接口中的方法
(2)语句
(3)配置
(4)通过session.getMapper()返回的对象不是实现类对象
@Test
public void test03(){
SqlSession sqlSession = MySessionUtils.getSession();
//返回的是实现接口的实现类对象
UserDao dao = sqlSession.getMapper(UserDao.class);
//删除
dao.deleteById(16);
//
sqlSession.commit();
sqlSession.close();
}
//com.wzx.dao.UserDao.findById
public interface UserDao {
//delete from user where id = 16;
void deleteById(int id);
}
<delete id="deleteById" parameterType="int">
delete from user where id = #{id};
</delete>
插入数据
- 定义接口中的方法
- 语句
- 配置
- 通过session.getMapper()返回的对象不是实现类对象
@Test
public void test04(){
SqlSession sqlSession = MySessionUtils.getSession();
//返回的是实现接口的实现类对象
UserDao dao = sqlSession.getMapper(UserDao.class);
User user = new User();
user.setId(100);
user.setUsername("jackjhone");
user.setAddress("北京");
user.setBirthday(new Date());
dao.saveUser(user);
//
sqlSession.commit();
sqlSession.close();
}