在研究一项技术之前,我们要想一下为什么会出现这种技术,对于本文要研究的Mybatis我们讨论一下他为什么会出现,他的出现又是为了解决什么问题的呢?
首先,我们来看一下传统的JDBC方式是如何工作的:
package cn.xd.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcTest {
/**
* @通过分析jdbc程序发现存在的问题
*/
public static void main(String[] args) {
//数据库连接
Connection connection = null;
//预编译的Statement,使用预编译的Statement提高数据库性能
PreparedStatement preparedStatement = null;
//结果 集
ResultSet resultSet = null;
try {
//1.加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2.通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "162636");
//3.定义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();
}
}
}
}
}
我们来分析一下其中存在的问题:
1、数据库的连接,使用时就创建,不使用立即释放,对数据库进行频繁连接开启和关闭,造成数据库资源浪费,影响 数据库性能。
解决设想:使用数据库连接池管理数据库连接。
2、将sql语句硬编码到java代码中,如果sql 语句修改,需要重新编译java代码,不利于系统维护。
解决设想:将sql语句配置在xml配置文件中,即使sql变化,不需要对java代码进行重新编译。
3、向preparedStatement中设置参数,对占位符号位置和设置参数值,硬编码在java代码中,不利于系统维护。
设想:将sql语句及占位符号和参数全部配置在xml中。
4、从resutSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,,不利于系统维护。
设想:将查询的结果集,自动映射成java对象。
补充:所谓的硬编码就是什么都在你的程序代码里面写死了,如果你想稍微修改一下效果,都得修改你的代码。
解决方式:Mybatis框架(当然还有其他的持久层框架也可以解决这些问题,hibernate等)
1、什么是Mybatis
MyBatis 是一个支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
注:Mybatis的着力点在于POJO和SQL之间的映射关系,而Hibernate(典型的ORM---对象关系映射框架)的着力点则在于POJO和数据库表之间的映射。
2.Mybatis的执行流程
(1)加载配置并初始化
触发条件:加载配置文件
处理过程:将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)接收调用请求
触发条件:调用Mybatis提供的API
传入参数:为SQL的ID和传入参数对象
处理过程:将请求传递给下层的请求处理层进行处理。
(3)处理操作请求
触发条件:API接口层传递请求过来
传入参数:为SQL的ID和传入参数对象
处理过程:
(A)根据SQL的ID查找对应的MappedStatement对象。
(B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。
(C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
(D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
(E)释放连接资源。
(4)返回处理结果将最终的处理结果返回。
3、下面我们按照流程进行一次简单的Mybatis开发演示
3.1、开发需求:根据用户id查询用户信息
3.2、开发环境准备
在数据库中建表(User表),建立web工程并将Mybatis需要的jar包导入工程中,将Mysql的驱动jar包导入工程中。
3.3 、开始编写示例程序(使用mapper接口方法)
工程结构如下图:
使用mapper接口开发应该遵循以下的规范:
1、在mapper.xml中的namespace等于mapper接口地址
2、mapper.java接口中的方法名和mapper.xml中statement的id一致
3、mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
4、mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。
下面是主要代码:
a、UserMapper.java
public interface UserMapper {
public User findUserById(int id) throws Exception;
}
b、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">
<mapper namespace="cn.xd.mybatis.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="user">
SELECT * FROM USER WHERE id=#{value}
</select>
</mapper>
可以分别对照一下上面的四条规范。
注意:不要忘记在SqlMapConfig的配置文件中配置UserMapper.xml,否则会报错:Type interface UserMapper is not known to the MapperRegistry.(还有一种情况会报这种错误,就是包名写错了。。。。)
3.4、编写测试程序,并执行(本文使用的是Junit测试,要加入Junit的jar包)
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
// mybatis配置文件
String resource = "SqlMapConfig.xml";
// 得到配置文件流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建会话工厂,传入mybatis的配置文件信息
sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
}
@Test
public void testFindUserById() throws Exception {
SqlSession sqlSession=sqlSessionFactory.openSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
User user=userMapper.findUserById(31);
System.out.println(user.getUsername());
}
}
至此,一个简单的Mybaits示例就完成了,同时可以看出Mybatis已经解决了上面Jdbc存在的问题。
最后要注意一点:
SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。
因此SqlSession的最佳应用场合在方法体内,定义成局部变量使用。