已经有jdbc了,为什么会出现mybatis等各种持久层框架
怎样自定义一个持久层框架
通过自定义持久层框架深入理解mybatis源码
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try{
//1 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2 获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf-8", "root", "123456");
//3 定义sql语句
String sql = "select * from t_user where user_name = ?";
//4 参数处理
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"杰克");
//5 获取结果集
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
Long userId = resultSet.getLong("user_id");
String userName = resultSet.getString("user_name");
User user = new User();
user.setUserId(userId);
user.setUserName(userName);
System.out.println(user);
}
//6 封装结果集
}catch (Exception e){
e.printStackTrace();
}finally {
// 7 释放
try{
resultSet.close();
preparedStatement.close();
connection.close();
}catch (SQLException e){
e.printStackTrace();
}
}
通过观察jdbc代码我们找到了问题以及解决办法
问题 | 解决办法 |
---|---|
数据库配置信息以及sql存在硬编码 | 通过配置文件解决 |
jdbc放在持久层会被反复调用。频繁创建释放数据库链接 | 连接池解决 |
手动封装结果集,繁琐 | 反射内省解决 |
将持久层框架看做一个项目,那么只需要考虑两点:
1使用端 2项目本身的实现
项目目的:封装jdbc并解决其产生的问题
提供两个配置信息:
为何要存放mapper.xml的路径?记住这个问题
<!-- 创建一个父节点 -->
<configuration>
<!-- 数据库配置信息-->
<dataSource>
<property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="jdbc" value="jdbc:mysql:///mybatis_test"></property>
<property name="name" value="root"></property>
<property name="password" value="123456"></property>
</dataSource>
<!-- 存放mapper.xml的全路径 -->
<mapper resource="mappers/UserMapper.xml"></mapper>
</configuration>
<!-- namespace 用于区分多个mapper -->
<mapper namespace="user">
<!-- namespace.id 组成sql的唯一标识 叫做statementId -->
<!-- resultType 返回类型 查询结果是什么,我们这边是引用类型User
那么就要给出User的全路径以供后续通过反射完成结果集的一个映射-->
<select id="selectList" resultType="com.hen84.entity.User">
select * from t_user
</select>
<!-- paramterType 参数类型,这里是多个参数所以是引用类型,同理给出全路径-->
<!-- 定义一个#{
} 用来获取到传入的参数是什么 然后将映射到的参数传递进去 -->
<select id="selectOne" resultType="com.hen84.entity.User" paramterType="com.hen84.entity.User">
select * from t_user
where user_id = #{
userId}
and user_name = #{
userName}
</select>
</mapper>
package com.hen84.io;
import java.io.InputStream;
public class Resources {
// 传入配置文件的路径,将其解析成输入流存到内存中
public static InputStream getResourceAsSteam(String path){
InputStream inputStream = Resources.class.getClassLoader().getResourceAsStream(path);
return inputStream;
}
}
package com.hen84.entity;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
// 用来存放对mybatis-config.xml文件的解析
public class Configuration {
// 数据库配置信息
DataSource dataSource;
// key: statementId :namespace.id 例如 user.selectList
// value: 封装好的mappedStatement对象
Map<String,MappedStatement> mappedStatementMap = new HashMap<String, MappedStatement>();
}
package com.hen84.entity;
// 用来存放对*Mapper.cml文件的解析
public class MappedStatement {
// id标识
private String id;
// 返回值类型
private String resultType;
// 参数类型
private String paramterType;
// sql语句
private String sql;
}
package com.hen84.test;
import com.hen84.io.Resources;
import com.hen84.sqlSession.SqlSessionFactory;
import com.hen84.sqlSession.SqlSessionFactoryBuilder;
import java.io.InputStream;
public class Test {
public void mybatisTest(){
try{
// 加载配置文件成字节输入流,存放到内存中
InputStream inputStream = Resources.getResourceAsSteam("mybatis-config.xml");
// 创建一个工厂SqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryBuilder.builder(inputStream);
}catch (Exception e){
}
}
}
package com.hen84.sqlSession;
import com.hen84.config.XMLConfigBuilder;
import com.hen84.entity.Configuration;
import org.dom4j.DocumentException;
import java.beans.PropertyVetoException;
import java.io.InputStream;
// 创建一个SqlSessionFactory构造器
public class SqlSessionFactoryBuilder {
public static SqlSessionFactory builder(InputStream inputStream) throws DocumentException, PropertyVetoException {
// 使用dom4j解析配置文件。将解析的内容封装到容器对象中
// 将此过程封装
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
Configuration configuration = xmlConfigBuilder.parseConfig(inputStream);
// 创建一个工厂SqlSessionFactory对象
return null;
}
}
package com.hen84.config;
import com.hen84.entity.Configuration;
import com.hen84.io.Resources;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
public class XMLConfigBuilder {
private Configuration configuration;
public XMLConfigBuilder() {
this.configuration = new Configuration();
}
// 使用dom4j对核心配置文件进行解析,封装到configuration中
public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException {
// dom4j将配置文件解析成一个document
Document document = new SAXReader().read(inputStream);
// getRootElement 拿到的就是 mybatis-config.xml中的父节点
Element rootElement = document.getRootElement();
// selectNodes 全局查询property节点 返回一个list 这里拿到的就是数据库配置信息
List<Element> propertyList = rootElement.selectNodes("//property");
// 创建一个Properties 用来存放拿到的数据库配置信息
Properties properties = new Properties();
for (Element element : propertyList) {
// name value 标签中的属性
String name = element.attributeValue("name");
String value = element.attributeValue("value");
properties.setProperty(name,value);
}
// 使用 c3p9 连接池
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(properties.getProperty("driver"));
comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbc"));
comboPooledDataSource.setUser(properties.getProperty("user"));
comboPooledDataSource.setPassword(properties.getProperty("password"));
// 核心配置信息存放到其容器中
configuration.setDataSource(comboPooledDataSource);
// 解析mapper.xml
// 拿到路径
List<Element> mapperList = rootElement.selectNodes("//mapper");
for (Element element : mapperList) {
String mapperResource = element.attributeValue("resource");
// 获取字节输入流
InputStream mapperAsSteam = Resources.getResourceAsSteam(mapperResource);
// 再一次封装解析 传入已经解析好核心配置信息的configuration对象
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
xmlMapperBuilder.parse(mapperAsSteam);
}
return configuration;
}
}
package com.hen84.config;
import com.hen84.entity.Configuration;
import com.hen84.entity.MappedStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
public class XMLMapperBuilder {
private Configuration configuration;
public XMLMapperBuilder(Configuration configuration) {
this.configuration = configuration;
}
// 使用dom4j对mapper配置文件进行解析,封装到configuration的MappedStatementMap中
public void parse(InputStream inputStream) throws DocumentException {
Document document = new SAXReader().read(inputStream);
// 此处拿到的就是 UserMapper.xml中的标签
Element rootElement = document.getRootElement();
// 拿到namespace唯一属性值
String nameSpace = rootElement.attributeValue("namespace");
// 获取下面的所有select节点返回list
List<Element> selectList = rootElement.selectNodes("//select");
for (Element element : selectList) {
String id = element.attributeValue("id");
String resultType = element.attributeValue("resultType");
String paramterTyoe = element.attributeValue("paramterTyoe");
String sqlText = element.getTextTrim();
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setResultType(resultType);
mappedStatement.setParamterType(paramterType);
mappedStatement.setSql(sqlText);
// namespaces.id 组成sql的唯一标识 叫做statementId
String statementId = nameSpace + "." + id;
// 将拿到的mappedStatement存放到configuration的map中 statementId 就是唯一标识
configuration.getMappedStatementMap().put(statementId,mappedStatement);
}
}
}
package com.hen84.sqlSession;
// 创建一个SqlSession工厂 生产SqlSession
public interface SqlSessionFactory {
// 生产sqlSession
public SqlSession openSession();
}
package com.hen84.sqlSession;
import com.hen84.entity.Configuration;
// 会话工厂接口的实现类
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configuration configuration;
// 创建会话工厂实例时将configuration带入
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
// 生产sqlSession
public SqlSession openSession() {
return new DefaultSqlSession();
}
}
package com.hen84.sqlSession;
// 会话接口 接口有对数据库的CRUD功能
public interface SqlSession {
}
package com.hen84.sqlSession;
// 会话接口的实现类
public class DefaultSqlSession implements SqlSession {
}
package com.hen84.sqlSession;
import com.hen84.entity.Configuration;
import com.hen84.entity.MappedStatement;
import java.util.List;
// 执行器
public interface Executor {
// 执行方法 configuration配置信息 mappedStatement 映射文件信息 params可变参数
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception;
}
package com.hen84.sqlSession;
import com.hen84.config.BoundSql;
import com.hen84.entity.Configuration;
import com.hen84.entity.MappedStatement;
import com.hen84.utils.GenericTokenParser;
import com.hen84.utils.ParameterMapping;
import com.hen84.utils.ParameterMappingTokenHandler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class SimpleExecutor implements Executor {
// 执行sql
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
// 获取数据库连接
Connection connection = configuration.getDataSource().getConnection();
// 获取sql 语句
String sql = mappedStatement.getSql();
// 转换sql 语句 对#{}的值解析存储
BoundSql boundSql = getBoundSql(sql);
// 通过 connection的prepareStatement方法 获取预处理对象 preparedStatement
// 参数就是我们已经解析好的sql
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
// 获取到参数类型的全路径
String paramterType = mappedStatement.getParamterType();
// 设置参数 params就是我们已经实例好的User
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
for (int i = 0 ; i < parameterMappings.size(); i++){
ParameterMapping parameterMapping = parameterMappings.get(i);
// content 就是#{}里面的值
String content = parameterMapping.getContent();
// 通过反射,完成参数的设置
Class<?> paramterTypeClass = getClassType(paramterType);
// 获取到属性对象
Field declaredField = paramterTypeClass.getDeclaredField(content);
// 暴力访问
declaredField.setAccessible(true);
// 拿到参数对象
Object o = declaredField.get(params[0]);
preparedStatement.setObject(i+1,o);
}
// 执行sql 结果封装在ResultSet中
ResultSet resultSet = preparedStatement.executeQuery();
ArrayList<Object> objects = new ArrayList<Object>();
String resultType = mappedStatement.getResultType();
Class<?> resultClass = getClassType(resultType);
// 封装结果集
while (resultSet.next()){
// 获取元数据
Object resultObject = resultClass.newInstance();
ResultSetMetaData metaData = resultSet.getMetaData();
for(int i = 1; i <= metaData.getColumnCount(); i++){
// 获取字段名
String columnName = metaData.getColumnName(i);
// 字段的值
Object object = resultSet.getObject(columnName);
// 通过反射 完成实体类和数据库表的映射 camelName 驼峰式命名下划线转换
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(camelName(columnName),resultClass);
Method writeMethod = propertyDescriptor.getWriteMethod();
// resultObject 类的实例 object 字段的值
writeMethod.invoke(resultObject, object);
}
objects.add(resultObject);
}
return (List<E>) objects;
}
// 解析sql语句 直接使用工具类 完成两个操作
// 1 将#{}转化为? 2将#{}的值进行存储
public BoundSql getBoundSql(String sql){
// 标记处理类 : 完成对占位符的解析处理工作
ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
/**
* 标记解析器
* params: openToken 开始标记 也就是#{
* closeToken 结束标记也就是 }
* TokenHandler
*/
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", tokenHandler);
// parse 将我们的sql 传入 最终返回一个解析之后的sql
String parse = genericTokenParser.parse(sql);
// getParameterMappings()获取道德ParameterMapping就是#{}里面的值的一个容器
// 比如 #{userId} 以及 #{userName}
List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();
return new BoundSql(parse,parameterMappings);
}
// 通过反射,获取到Class
public Class<?> getClassType(String paramterType) throws ClassNotFoundException {
if(paramterType != null) return Class.forName(paramterType);
throw new RuntimeException("ClassNotFoundException:" + paramterType);
}
/**
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
* 例如:HELLO_WORLD->HelloWorld
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String camelName(String name) {
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty()) {
// 没必要转换
return "";
} else if (!name.contains("_")) {
// 不含下划线,仅将首字母小写
return name.substring(0, 1).toLowerCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String camels[] = name.split("_");
for (String camel : camels) {
// 跳过原始字符串中开头、结尾的下换线或双重下划线
if (camel.isEmpty()) {
continue;
}
// 处理真正的驼峰片段
if (result.length() == 0) {
// 第一个驼峰片段,全部字母都小写
result.append(camel.toLowerCase());
} else {
// 其他的驼峰片段,首字母大写
result.append(camel.substring(0, 1).toUpperCase());
result.append(camel.substring(1).toLowerCase());
}
}
return result.toString();
}
}
package com.hen84.config;
import com.hen84.utils.ParameterMapping;
import java.util.ArrayList;
import java.util.List;
// 用来存放解析之后的sql 也就是将#{} 转化为?之后的sql
public class BoundSql {
// 解析之后的sql
private String sql;
// #{}里面的值
private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
public BoundSql(String sql, List<ParameterMapping> parameterMappings) {
this.sql = sql;
this.parameterMappings = parameterMappings;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
public void setParameterMappings(List<ParameterMapping> parameterMappings) {
this.parameterMappings = parameterMappings;
}
}
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hen84.utils;
/**
* @author Clinton Begin
*/
public class GenericTokenParser {
private final String openToken; //开始标记
private final String closeToken; //结束标记
private final TokenHandler handler; //标记处理器
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
/**
* 解析${}和#{}
* @param text
* @return
* 该方法主要实现了配置文件、脚本等片段中占位符的解析、处理工作,并返回最终需要的数据。
* 其中,解析工作由该方法完成,处理工作是由处理器handler的handleToken()方法来实现
*/
public String parse(String text) {
// 验证参数问题,如果是null,就返回空字符串。
if (text == null || text.isEmpty()) {
return "";
}
// 下面继续验证是否包含开始标签,如果不包含,默认不是占位符,直接原样返回即可,否则继续执行。
int start = text.indexOf(openToken, 0);
if (start == -1) {
return text;
}
// 把text转成字符数组src,并且定义默认偏移量offset=0、存储最终需要返回字符串的变量builder,
// text变量中占位符对应的变量名expression。判断start是否大于-1(即text中是否存在openToken),如果存在就执行下面代码
char[] src = text.toCharArray();
int offset = 0;
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) {
// 判断如果开始标记前如果有转义字符,就不作为openToken进行处理,否则继续处理
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(openToken);
offset = start + openToken.length();
} else {
//重置expression变量,避免空指针或者老数据干扰。
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + openToken.length();
int end = text.indexOf(closeToken, offset);
while (end > -1) {
存在结束标记时
if (end > offset && src[end - 1] == '\\') {
//如果结束标记前面有转义字符时
// this close token is escaped. remove the backslash and continue.
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {
//不存在转义字符,即需要作为参数进行处理
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
break;
}
}
if (end == -1) {
// close token was not found.
builder.append(src, start, src.length - start);
offset = src.length;
} else {
//首先根据参数的key(即expression)进行参数处理,返回?作为占位符
builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
}
package com.hen84.utils;
// 用来存储#{} 里面的值的存储
public class ParameterMapping {
private String content;
public ParameterMapping(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
package com.hen84.utils;
import java.util.ArrayList;
import java.util.List;
public class ParameterMappingTokenHandler implements TokenHandler {
private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
// context是参数名称 #{id} #{username}
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
return "?";
}
private ParameterMapping buildParameterMapping(String content) {
ParameterMapping parameterMapping = new ParameterMapping(content);
return parameterMapping;
}
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
public void setParameterMappings(List<ParameterMapping> parameterMappings) {
this.parameterMappings = parameterMappings;
}
}
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hen84.utils;
/**
* @author Clinton Begin
*/
public interface TokenHandler {
String handleToken(String content);
}
package com.hen84.test;
import com.hen84.entity.User;
import com.hen84.io.Resources;
import com.hen84.sqlSession.SqlSession;
import com.hen84.sqlSession.SqlSessionFactory;
import com.hen84.sqlSession.SqlSessionFactoryBuilder;
import java.io.InputStream;
import java.util.List;
public class Test {
public static void main(String[] args) {
try{
// 加载配置文件成字节输入流,存放到内存中
InputStream inputStream = Resources.getResourceAsSteam("mybatis-config.xml");
// 创建一个工厂SqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryBuilder.builder(inputStream);
// 拿到会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建一个User实体 用于传参
User user = new User();
user.setUserId(2L);
user.setUserName("胡歌");
// 调用查询方法
List<User> resultUser = sqlSession.selectList("user.selectList",user);
// 调用单个查询方法
User resultUserOne = sqlSession.selectOne("user.selectOne", user);
System.out.println(resultUser);
System.out.println(resultUserOne);
}catch (Exception e){
e.printStackTrace();
}
}
}
执行结果
[User{
userId=1, userName='彭于晏', userPhone='10000009282'}, User{
userId=2, userName='胡歌', userPhone='1999872625384'}]
User{
userId=2, userName='胡歌', userPhone='1999872625384'}
到此算是大致OK了,但是还是会有两个问题
优化过程 :动态代理返回代理对象
由代理对象去解决我们的硬编码问题
package com.hen84.dao;
import com.hen84.entity.User;
import java.util.List;
public interface UserMapper {
List<User> selectList();
User selectOne(User user);
}
我们在SqlSession接口以及实现类加入getMapper的方法和实现
// 返回动态代理的实现类 参数就是我们的dao
public <T> T getMapper(Class<?> mapperClass);
@Override
public <T> T getMapper(Class<?> mapperClass) {
// 使用JDK 动态代理 来为接口生成代理对象
/**
*
* newProxyInstance
* 参数:类加载器
* Class数组
* InvocationHandler接口
* InvocationHandler接口实现里面有一个invoke方法
*
* invoke
* 参数: proxy 当前代理对象
* method 当前被调用方法
* args 传递的参数
*
*/
Object o = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{
mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 根据不同情况来调用selectList 或者selectOne
//准备参数
// 1、statementId
/**
* 这里我们考虑一个问题
* statementId 也就是mapper.xml中的namespace.id 我们目前没法拿到
* 于是我们更改namespace的值为我们接口的全路径 例如com.hen84.dao.UserMapper
* id 我们这里一定要和方法名保持一致
*/
// methodName 也就是id
String methodName = method.getName();
// className 也就是namespace
String className = method.getDeclaringClass().getName();
String statementId = className + "." + methodName;
// 2、params 就是args
// 如何来确定调用的是selectList 还是selectOne ? 返回值类型
// 获取被调用方法的返回值类型
Type genericReturnType = method.getGenericReturnType();
// 判断是否进行了泛型类型参数化
// 泛型类型参数化:当前返回值类型是否含有泛型 有就是List
if(genericReturnType instanceof ParameterizedType){
List<Object> objects = selectList(statementId, args);
return objects;
}
return selectOne(statementId,args);
}
});
return (T) o;
}
public static void main(String[] args) {
try{
// 加载配置文件成字节输入流,存放到内存中
InputStream inputStream = Resources.getResourceAsSteam("mybatis-config.xml");
// 创建一个工厂SqlSessionFactory
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryBuilder.builder(inputStream);
// 拿到会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 创建一个User实体 用于传参
User user = new User();
user.setUserId(2L);
user.setUserName("胡歌");
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectList();
System.out.println(users);
User user1 = userMapper.selectOne(user);
System.out.println(user1);
}catch (Exception e){
e.printStackTrace();
}
}
打印结果
[User{
userId=1, userName='彭于晏', userPhone='10000009282'}, User{
userId=2, userName='胡歌', userPhone='1999872625384'}]
User{
userId=2, userName='胡歌', userPhone='1999872625384'}
OK 到此自定义持久层框架就结束了
源码在最后
1、通过自定义持久层框架加深了对mybatis源码的理解
2、所有的持久层框架都是对JDBC的一个高级封装
3、mybatis中的部分重要接口的作用
4、mybatis设计模式的一个深入理解
源码:
链接: https://pan.baidu.com/s/1Rk0CzAnVol11O3iIHlB3UA 密码: o7s9