MyBatis(一)Mybatis入门
目录
- 一、概述
- 二、JDBC编程的分析
- jdbc问题分析:
- 三、mybatis环境搭建
- 前期准备
- ① 创建一个maven工程
- ② 创建一个数据库,供我们实验使用:
- 搭建mybatis开发环境
- ① 导入依赖
- ② 创建User类:
- ③ 编写持久层接口`IUserDao`:
- ④ 编写sqlMapConfig.xml配置文件:
- ⑤ 编写持久层接口的映射文件 `IUserDao.xml:`
- 总结
- 测试我们的demo
- ①log4j的配置文件
- ② 测试入口:
- ③ 运行结果
- 总结
- 四、使用注解
- 五、写Dao实现类
- 六、分析
- mybatis入门案例中设计模式的分析
- 自定义mybatis的分析
- 创建代理对象的分析
一、概述
mybatis是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql语句本身, 而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis通过xml 或注解的方式将要执行的各种statement配置起来,并通过java对象和statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并 返回。
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc进行了封装,屏蔽了 jdbc api 底层访问细节,使我 们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
了我们能够更好掌握框架运行的内部过程,并且有更好的体验,下面我们将从自定义 Mybatis 框架开始来 学习框架。此时我们将会体验框架从无到有的过程体验,也能够很好的综合前面阶段所学的基础。
二、JDBC编程的分析
我们回顾一下以前的JDBC编程方式:
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8",
"root", "0000");
// 定义 sql 语句 ?表示占位符
String sql = "select * from user where username = ?";
// 获取预处理 statement
preparedStatement = connection.prepareStatement(sql);
// 设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的 参数值
preparedStatement.setString(1, "王五");
// 向数据库发出 sql 执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
// 遍历查询结果集
while (resultSet.next()) {
System.out.println(resultSet.getString("id") + "," + resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
上边使用 jdbc 的原始方法(未经封装)实现了查询数据库表记录的操作。
jdbc问题分析:
- 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
- Sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。
- 使用 preparedStatement 向占有位符号传参数存在硬编码,因为 sql 语句的 where 条件不一定,可能 多也可能少,修改 sql 还要修改代码,系统不易维护。
- 对结果集解析存在硬编码(查询列名),sql 变化导致解析代码变化,系统不易维护,如果能将数据库记 录封装成 pojo 对象解析比较方便。
三、mybatis环境搭建
接下来,我们做一个mybatis的小demo,整体上感受一下mybatis的使用过程。
1. 前期准备
① 创建一个maven工程
② 创建一个数据库,供我们实验使用:
新建一张表:
CREATE TABLE `user` (
`id` INT ( 11 ) NOT NULL auto_increment,
`username` VARCHAR ( 32 ) NOT NULL COMMENT '用户名称',
`birthday` datetime DEFAULT NULL COMMENT '生日',
`sex` CHAR ( 1 ) DEFAULT NULL COMMENT '性别',
`address` VARCHAR ( 256 ) DEFAULT NULL COMMENT '地址',
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8;
插入一些内容:
INSERT INTO `user` ( `id`, `username`, `birthday`, `sex`, `address` )
VALUES
( 41, '刘帅帅', '2018-02-27 17:47:08', '男', '菏泽' ),
( 42, '刘伟佳', '2018-03-02 15:09:37', '男', '青岛' ),
( 43, '李萌萌', '2018-03-04 11:34:34', '女', '济南' ),
( 45, '李美美', '2018-03-04 12:04:06', '女', '滨州' ),
( 46, '外星人', '2018-03-07 17:37:26', '男', '火星' ),
( 48, '歪比巴卜', '2018-03-08 11:44:00', '女', '三体' );
2. 搭建mybatis开发环境
① 导入依赖
我们在maven工程中导入依赖,并把项目的打包方式改为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.veeja</groupId>
<artifactId>day01_01mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
② 创建User类:
package com.veeja.domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
③ 编写持久层接口IUserDao
:
package com.veeja;
import com.veeja.domain.User;
import java.util.List;
/** * 用户的持久层接口 */
public interface IUserDao {
/** * 查询所有的用户 * * @return */
List<User> findAll();
}
④ 编写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">
<!--mybatis的主配置文件-->
<configuration>
<!--配置环境-->
<environments default="mysql">
<!--配置mysql的环境-->
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据源(连接池)-->
<dataSource type="POOLED">
<!--配置连接数据库的4个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdatabase"/>
<property name="username" value="root"/>
<property name="password" value="0000"/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="com/veeja/dao/IUserDao.xml"/>
</mappers>
</configuration>
⑤ 编写持久层接口的映射文件 IUserDao.xml:
注意:
创建位置:必须和持久层接口在相同的包中。
名称:必须以持久层接口名称命名文件名,扩展名是.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.veeja.dao.IUserDao">
<!-- 配置查询所有的操作 -->
<select id="findAll" resultType="com.veeja.domain.User">
select * from user
</select>
</mapper>
总结
接下来我们总结一下,搭建一个mybatis的环境需要做些什么?
- 创建一个maven工程并且导入依赖;
- 创建实体类和DAO接口
- 创建mybatis的主配置文件,SqlMapConfig.xml
- 创建映射配置文件,IUserDao.xml
这里面有几个注意事项:
第一个:创建IUserDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保持一致。
在Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper
所以:IUserDao 和 IUserMapper是一样的
第二个:在idea中创建目录的时候,它和包是不一样的
包在创建时:com.itheima.dao它是三级结构
目录在创建时:com.itheima.dao是一级目录
第三个:mybatis的映射配置文件位置必须和dao接口的包结构相同
第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名
当我们遵从了第三,四,五点之后,我们在开发中就无须再写dao的实现类。
3. 测试我们的demo
①log4j的配置文件
首先导入log4j的配置文件,在resources文件夹下创建文件:log4j.properties
内容如下:
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
② 测试入口:
package com.veeja.test;
import com.veeja.dao.UserDaoImpl;
import com.veeja.domain.User;
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.InputStream;
import java.util.List;
/** * mybatis的入门案例使用main函数执行 */
public class MybatisTest {
/** * 入门案例 */
public static void main(String[] args) throws Exception {
// 1. 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 创建一个SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
// 3. 使工厂生产SQLSession对象
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();
in.close();
}
}
③ 运行结果
运行一下,结果符合我们预期。
总结
我们再总结一下刚才在测试类里面我们都做了什么。
第一步:读取配置文件
第二步:创建SqlSessionFactory工厂
第三步:创建SqlSession
第四步:创建Dao接口的代理对象
第五步:执行dao中的方法
第六步:释放资源
四、使用注解
我们发现,这样好像还是很麻烦,还要写xml文件,我们在上面的概述中提到,mybatis可以使用xml或者注解的方式来进行配置。接下来我们演示一下使用注解的配置方式。
我们新建一个maven工程。
为了方便,我们把上个项目中的src下的main和test文件夹以及pom.xml文件直接复制到新的项目中去。
我们到底怎么样使用注解呢?
既然我们使用注解,那么上面的一些xml配置文件就没什么用了。
IUserDao.xml文件我们就可以直接删除了。
然后在我们的UserDaoImpl.java类的相应方法上,我们写上一些注解:
/** * 用户的持久层接口 */
public interface IUserDao {
/** * 查询所有的用户 * * @return */
@Select("select * from user")
List<User> findAll();
}
但是这样有一个问题,我们在SqlMapConfig.xml中有一个mapper,指定了配置文件的位置,
那么现在这个配置文件没有了,我们该怎么办呢?
如果是注解的方式我们应该使用class属性, 而不是resource属性,也就是:
<mappers>
<mapper class="com.veeja.dao.IUserDao"/>
</mappers>
我们按照上面的测试类继续运行一遍,还是完全没有问题的。
我们可以发现,这样的方式会简单一些。
我们稍微总结一下使用注解的方式:
把UserDaoImpl.xml
移除,在dao接口的方法上使用@Select
注解,并且指定SQL语句。
同时需要在SqlMapConfig.xml
中的mapper配置时,使用class属性指定dao接口的全限定类名。
五、写Dao实现类
我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式,不管使用XML还是注解配置。
但是Mybatis它是支持写dao实现类的。 接下来我们演示一下。
首先创建一个新的工程:
我们还是把第一个工程中的main和test直接复制到新的项目中来。还要复制pom.xml中的配置进来。
接下来我们在dao文件夹下新建一个包impl
,并且添加一个新的IUserDao的实现类UserDaoImpl
:
我们要在Dao的实现类中添加一下代码:
public class UserDaoImpl implements IUserDao {
private SqlSessionFactory factory;
/** * 覆写构造参数 * @param factory */
public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
public List<User> findAll() {
//1.使用工厂创建SQLSession对象
SqlSession session = factory.openSession();
//2. 使用session执行查询方法
// 为了定位这个方法,使用了namespace.function的方式。
List<User> users = session.selectList("com.veeja.dao.IUserDao.findAll");
session.close();
//3.返回查询结果
return users;
}
}
我们在测试的时候需要作出一点改变,我们的dao是需要一个factory的,我们应该传给它,而且不需要session了:
public class MybatisTest {
/** * 入门案例 */
public static void main(String[] args) throws Exception {
// 1. 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 创建一个SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
/******************************************************/
// 3. 使用工厂创建dao对象
IUserDao userDao = new UserDaoImpl(factory);
/******************************************************/
// 4. 使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
// 5. 释放资源
in.close();
}
}
运行结果完全符合预期:
但是这样的写法毫无意义,增加了我们开发的任务量,但是需要注意的上面就是在Dao的实现中为了定位相应 的方法,使用了namespace和方法名的方式。
六、分析
1. mybatis入门案例中设计模式的分析
2. 自定义mybatis的分析
mybatis在使用代理Dao的方式实现增删改查时做什么事呢?
只有两件事,一个是创建代理对象,一个是在代理对象中调用selectList()
方法,而里面是具体怎么执行的呢?看如下的图示:
3. 创建代理对象的分析
END.
还没有评论,来说两句吧...