前言
Mybatis就是一个封装来jdbc的持久层及框架,它让程序员只关注sql本身,而不需要去关注如连接的创建,statment的创建等操作,它会将输入参数,输出结果进行映射。
**持久层:**数据保持在数据库或者硬盘一类可以保存很长时间的设备里面,不像放在内存中那样断电就消失了,也就是把数据存在持久化设备上。
问题1:在用mybatis框架时,只写了接口,没写实现类,拿到对象,对象如何来的?
问题2:在用mybatisn框架,那个类帮你实现数据库连接,执行sql,遍历结果集
正文
一、Mybatis框架执行流程
思路明确了,那么设计一下简化版的mybatis框架来完成一个查询。
二、简化Mybatis
项目结构图:
1.连接数据库
配置环境:两个jar包–>mysql-connector-java-5.1.32.jar和jaxen-1.1-beta-11.jar
思路:
配置文件有两种,一个为主配置文件,一个为映射文件。
主配置文件:配置了jdbc等环境信息。
映射文件:配置了接口对应的sql语句映射。
主配置文件代码:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<dataSource>
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/testdb"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
<mappers>
<mapper resource="UserMapper.xml"></mapper>
<mapper resource="ItemMapper.xml"></mapper>
</mappers>
</configuration>
映射sql文件代码:
<?xml version="1.0" encoding="UTF-8" ?>
<select id="com.tedu.UserMapper.selectAll" resultType="com.tedu.User">
select * from user
</select>
将xml文件解析出来:
package org.mybatis.configuration;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
public class XmlConfigParser {
public static Configuration configuration=new Configuration();
public static void parser(InputStream inputStream)throws Throwable{
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(inputStream);
List<Element> list = document.selectNodes("//property");
for (Element element:list){
String valueOfname = element.attributeValue("name");
String valueOfvalue = element.attributeValue("value");
switch (valueOfname){
case "driver":
configuration.setDriver(valueOfvalue);
break;
case "url":
configuration.setUrl(valueOfvalue);
break;
case "username":
configuration.setUsername(valueOfvalue);
break;
case "password":
configuration.setPassword(valueOfvalue);
break;
}
}
//解析mapper.xml
HashMap<String, SqlMapper> hashMap = new HashMap<>();
configuration.setSqlMappers(hashMap);
List<Element> mapperList = document.selectNodes("//mapper");
for (Element element:mapperList){
String mapperFileName = element.attributeValue("resource");
parserMapper(mapperFileName);
}
System.out.println(configuration.toString());
}
private static void parserMapper(String mapperFileName) throws Throwable{
ClassLoader classLoader = XmlConfigParser.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(mapperFileName);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(inputStream);
List<Element> list = document.selectNodes("//select");
for (Element element:list){
String id = element.attributeValue("id");
String resultType = element.attributeValue("resultType");
String sql = element.getText();
SqlMapper sqlMapper = new SqlMapper(id, resultType, sql);
configuration.getSqlMappers().put(id,sqlMapper);
}
}
}
2、动态代理
环境配置:dom4j-1.6.1.jar
思路:sqlSession肯定不会自己去执行,因为不能写死所以使用动态代理来使代理类去实现具体方法。
package org.mybatis.executor;
import java.lang.reflect.Proxy;
public class SqlSession {
public Object getMapper(Class clazz) throws Throwable {
ClassLoader classLoader = clazz.getClassLoader();
Class[] interfaces={clazz};
MapperProxy mapperProxy = new MapperProxy();
Object proxyObject = Proxy.newProxyInstance(classLoader, interfaces, mapperProxy);
return proxyObject;
}
}
package org.mybatis.executor;
import org.mybatis.configuration.Configuration;
import org.mybatis.configuration.SqlMapper;
import org.mybatis.configuration.XmlConfigParser;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
public class MapperProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke method="+method.getName() );
//1,得到id
String methodName=method.getName();
String interfaceName = method.getDeclaringClass().getName();
String id=interfaceName+"."+methodName;
//2,从xmlConfigParser中得到configuration
Configuration configuration = XmlConfigParser.configuration;
//3,连接数据库
//3.1加载驱动
Class.forName(configuration.getDriver());
//3.2 建立连接
Connection connection = DriverManager.getConnection(configuration.getUrl(),
configuration.getUsername(),
configuration.getPassword());
//4,执行sql
SqlMapper sqlMapper = configuration.getSqlMappers().get(id);
String sql = sqlMapper.getSql();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//5,得到结果集
ResultSet resultSet = preparedStatement.executeQuery();
//6,遍历结果集
int row=0;
ArrayList<Object> list = new ArrayList<>();
while (resultSet.next()){
row++;
//6.1创建实体类对象
String resultType = sqlMapper.getResultType();
Class clazz = Class.forName(resultType);
Object entity = clazz.newInstance();
//6.2给对象属性赋值
/*
id,username
1,root
entity{id=null,username=null}
*/
// 6.2.1 取列的信息
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
//6.2.1 遍历列
for(int i=1;i<=columnCount;i++){
//取列名
String columnName = metaData.getColumnName(i);
System.out.print(columnName+"|");
//取列的值
Object dbData = resultSet.getObject(columnName);
//根据列名创建field(entity的属性)
Field field = clazz.getDeclaredField(columnName);
//给属性赋值
field.setAccessible(true);//private-->public
//entity.field=dbData
field.set(entity,dbData);
}
System.out.println("");
//6.3把对象放在集合中
list.add(entity);
}
//7,返回list
return list;
}
}
3、executor执行
准备:先创建两个实体类,一个是对应接口实体类,一个是xml文件sql实体类
sql实体类:
package com.tedu;
//实体类
public class User {
Integer id;
String username;
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
对应接口实体类:
package com.tedu;
import java.util.List;
public interface UserMapper {
public List<User> selectAll();
}
4、测试类
package org.mybatis;
import com.tedu.Item;
import com.tedu.ItemMapper;
import com.tedu.User;
import com.tedu.UserMapper;
import org.mybatis.configuration.SqlMapper;
import org.mybatis.configuration.XmlConfigParser;
import org.mybatis.executor.SqlSession;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
public static void main(String[] args) throws Throwable{
ClassLoader classLoader = MybatisTest.class.getClassLoader();
InputStream inputStream =
classLoader.getResourceAsStream("mybatis-config.xml");
XmlConfigParser.parser(inputStream);
SqlSession sqlSession = new SqlSession();
Object proxyObject = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper= (UserMapper) proxyObject;
List<User> list = userMapper.selectAll();
System.out.println(list);
Object itemProxy = sqlSession.getMapper(ItemMapper.class);
ItemMapper itemMapper= (ItemMapper) itemProxy;
List<Item> items = itemMapper.selectAll();
System.out.println(items);
}
}
总结:
-
连接数据库的初始化工作
ect;
List list = userMapper.selectAll();
System.out.println(list);Object itemProxy = sqlSession.getMapper(ItemMapper.class); ItemMapper itemMapper= (ItemMapper) itemProxy; List<Item> items = itemMapper.selectAll(); System.out.println(items);
}
}
## 总结:
- 连接数据库的初始化工作
- 建立xml配置文件的关系,使得我们调用方法就可以自动执行xml文件中的SQL语句