小小程序猿
这个人很懒,什么都没写
Toggle navigation
小小程序猿
主页
关于
友链
归档
标签
手写一个迷你版的Mybatis
源码
2021-08-31 22:41:52
93
0
0
terry
源码
# 手写一个迷你版的Mybatis ## 流程分析 + 读取Mybatis配置文件 + 构建SqlSessionFactory + 解析 Mybatis配置文件 + 获取 MySqlSessionFactory + 获取SqlSession + 解析mapper.xml的sql参数 + 使用反射封装数据库查询到的内容 + 获取执行器并使用执行器进行数据库操作 + 获取sqlSession + 获取Mapper接口 + 调用Mapper接口对象的方法操作数据库并封装返回结果 ## 包结构 + com.one.mapper 测试接口 + com.one.model 测试数据库实体 + com.one.mybatis 框架 + executor 执行器 + io 文件 + mapping 配置 + parsing xml + pool 连接池 + proxy 动态代理 + reflection 反射 + session 会话 ## 客户端调用 ```java package com.one; import com.one.mapper.UserInfoMapper; import com.one.model.UserInfo; import com.one.mybatis.io.MyResources; import com.one.mybatis.session.MySqlSession; import com.one.mybatis.session.MySqlSessionFactory; import com.one.mybatis.session.MySqlSessionFactoryBuilder; import java.io.InputStream; public class Test { public static void main(String[] args) { //第一步:读取mybatis-config.xml配置文件 InputStream inputStream = MyResources.getResourceAsStream("mybatis-config.xml"); //第二步:构建SqlSessionFactory (框架初始化) MySqlSessionFactory mySqlSessionFactory = new MySqlSessionFactoryBuilder().build(inputStream); //第三步:打开SqlSession MySqlSession mySqlSession = mySqlSessionFactory.openMySqlSession(); //第四步:获取Mapper接口对象 (底层是动态代理) UserInfoMapper userInfoMapper = mySqlSession.getMapper(UserInfoMapper.class); //第五步:调用Mapper接口对象的方法操作数据库; UserInfo userInfo = userInfoMapper.selectByPrimaryKey(1); System.out.println(userInfo.getUserType()); } } ``` ## 框架的实现 ### SQL脚本 + u_user_info ```sql CREATE TABLE `u_user_info` ( `id` int NOT NULL AUTO_INCREMENT, `user_name` varchar(50) DEFAULT NULL, `create_time` date DEFAULT NULL, `user_type` tinyint(1) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; ``` ### 1、读取配置文件 + mybatis-config.xml dtd文件格式规范 ```xml <!-- ?表示出现0次或1次 * 表示出现0次或n次 + 表示至少出现1次,dtd的xml约束是有顺序的 ,&用& --> <?xml version="1.0" encoding="UTF-8" ?> <!ELEMENT configuration (environment?, mappers?)> <!ELEMENT environment (dataSource+)> <!ATTLIST environment default CDATA #REQUIRED > <!ELEMENT dataSource (property*)> <!ATTLIST dataSource id CDATA #REQUIRED > <!ELEMENT property EMPTY> <!ATTLIST property name CDATA #REQUIRED value CDATA #REQUIRED > <!ELEMENT mappers (mapper*)> <!ELEMENT mapper EMPTY> <!ATTLIST mapper resource CDATA #IMPLIED > ``` + UserInfoMapper.xml dtd文件格式规范 ```xml <?xml version="1.0" encoding="UTF-8" ?> <!ELEMENT configuration (environment?, mappers?)> <!ELEMENT environment (dataSource+)> <!ATTLIST environment default CDATA #REQUIRED > <!ELEMENT dataSource (property*)> <!ATTLIST dataSource id CDATA #REQUIRED > <!ELEMENT property EMPTY> <!ATTLIST property name CDATA #REQUIRED value CDATA #REQUIRED > <!ELEMENT mappers (mapper*)> <!ELEMENT mapper EMPTY> <!ATTLIST mapper resource CDATA #IMPLIED > ``` + mybatis-config.xml配置文件 ```xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration SYSTEM "src/main/java/com/one/mybatis/parsing/mybatis-1.0-config.dtd"> <configuration> <environment default="dev"> <dataSource id="dev"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="12345678"/> </dataSource> </environment> <mappers> <mapper resource="mapper/UserInfoMapper.xml"/> </mappers> </configuration> ``` + UserInfoMapper.xml文件 ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper SYSTEM "src/main/java/com/one/mybatis/parsing/mybatis-1.0-mapper.dtd"> <mapper namespace="com.one.mapper.UserInfoMapper"> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultType="com.one.model.UserInfo"> select * from u_user_info where id = #{id, jdbcType=INTEGER} </select> </mapper> ``` + 读取文件得到InputStream,完成第一步流程 ```java package com.one.mybatis.io; import java.io.InputStream; // 读取资源转成InputStream public class MyResources { public static InputStream getResourceAsStream(String resource) { // 使用类加载器加载资源返回输入流 return ClassLoader.getSystemClassLoader().getResourceAsStream(resource); } } ``` ### 2、构建SqlSessionFactory #### 2.1 解析配置文件封装到MyConfiguration + MyConfiguration ```java package com.one.mybatis.mapping; import java.util.Map; // mybatis-config.xml 配置文件封装 public class MyConfiguration { // configuration/environment private MyEnvironment myEnvironment; // configuration/mappers private Map<String, MyMapperStatement> myMapperStatementMap; public MyEnvironment getMyEnvironment() { return myEnvironment; } public void setMyEnvironment(MyEnvironment myEnvironment) { this.myEnvironment = myEnvironment; } public Map<String, MyMapperStatement> getMyMapperStatementMap() { return myMapperStatementMap; } public void setMyMapperStatementMap(Map<String, MyMapperStatement> myMapperStatementMap) { this.myMapperStatementMap = myMapperStatementMap; } } ``` + MyEnvironment ```java package com.one.mybatis.mapping; // 封装数据库链接信息 public class MyEnvironment { private String driver; private String url; private String username; private String password; public String getDriver() { return driver; } public void setDriver(String driver) { this.driver = driver; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } ``` + MyMapperStatement ```java package com.one.mybatis.mapping; // 封装mapper.xml的每一条SQL public class MyMapperStatement { private String namespace;//命名空间 private String sqlId;//sqlid private String parameterType;//参数类型 private String resultType;//返回类型 private String sql;//sql语句 public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getSqlId() { return sqlId; } public void setSqlId(String sqlId) { this.sqlId = sqlId; } public String getParameterType() { return parameterType; } public void setParameterType(String parameterType) { this.parameterType = parameterType; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } } ``` + MyXMLEntityResolver ```java package com.one.mybatis.parsing; import com.one.mybatis.io.MyResources; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.io.InputStream; import java.util.Locale; // dtd验证 public class MyXMLEntityResolver implements EntityResolver { private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-1.0-config.dtd"; private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-1.0-mapper.dtd"; private static final String MYBATIS_CONFIG_DTD = "com/one/mybatis/parsing/mybatis-1.0-config.dtd"; private static final String MYBATIS_MAPPER_DTD = "com/one/mybatis/parsing/mybatis-1.0-mapper.dtd"; @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException { try { if (systemId != null) { String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH); //mybatis-1.0-config.dtd if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM)) { return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId); //mybatis-1.0-mapper.dtd } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM)) { return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId); } } return null; } catch (Exception e) { throw new SAXException(e.toString()); } } private InputSource getInputSource(String path, String publicId, String systemId) { InputSource source = null; if (path != null) { try { InputStream in = MyResources.getResourceAsStream(path); source = new InputSource(in); source.setPublicId(publicId); source.setSystemId(systemId); } catch (Exception e) { e.printStackTrace(); } } return source; } } ``` + MyXPathParser ```java package com.one.mybatis.parsing; import org.w3c.dom.Document; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import java.io.InputStream; public class MyXPathParser { private XPath xpath; private Document document; public MyXPathParser(InputStream inputStream) { this.xpath = getXpath(); this.document = createDocument(new InputSource(inputStream)); } public XPath getXpath() { //XPath方式的xml解析对象 XPathFactory factory = XPathFactory.newInstance(); return factory.newXPath(); } // 创建Document对象,jdk自带dom解析 private Document createDocument(InputSource inputSource) { try { //JDK提供的文档解析工厂对象 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); //设置是否支持命名空间 factory.setNamespaceAware(false); //设置是否忽略注释 factory.setIgnoringComments(true); //设置是否忽略元素内容的空白 factory.setIgnoringElementContentWhitespace(false); //是否将CDATA节点转换为文本节点 factory.setCoalescing(false); //设置是否展开实体引用节点,这里是sql片段引用 factory.setExpandEntityReferences(true); //创建一个DocumentBuilder对象 DocumentBuilder builder = factory.newDocumentBuilder(); //设置解析mybatis xml文档节点的解析器,也就是上面的XMLMapperEntityResolver builder.setEntityResolver(new MyXMLEntityResolver()); //设置验证 factory.setValidating(true); //设置解析文档错误的处理 builder.setErrorHandler(new ErrorHandler() { @Override public void error(SAXParseException exception) throws SAXException { throw exception; } @Override public void fatalError(SAXParseException exception) throws SAXException { throw exception; } @Override public void warning(SAXParseException exception) throws SAXException { } }); //解析输入源的xml数据为一个Document文档对象 return builder.parse(inputSource); } catch (Exception e) { throw new RuntimeException("Error creating document instance. Cause: " + e, e); } } // 在一个指定的上下文文档中 评估、评价、计算 一个XPath表达式的值,并返回指定的类型 public Object evaluate(String expression) { try { return xpath.evaluate(expression, document, XPathConstants.NODE); } catch (Exception e) { throw new RuntimeException("Error evaluating XPath. Cause: " + e, e); } } } ``` + MyXMLConfigBuilder ```java package com.one.mybatis.parsing; import com.one.mybatis.io.MyResources; import com.one.mybatis.mapping.MyConfiguration; import com.one.mybatis.mapping.MyEnvironment; import com.one.mybatis.mapping.MyMapperStatement; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.InputStream; import java.util.HashMap; import java.util.Map; public class MyXMLConfigBuilder { private MyXPathParser myXPathParser; public MyXMLConfigBuilder(InputStream inputStream) { myXPathParser = new MyXPathParser(inputStream); } // 解析,返回MyConfiguration配置对象 public MyConfiguration parse() { //数据源连接信息的Map Map<String, String> dataSourceMap = new HashMap<String, String>(); //存放mapper.xml的sql信息 Map<String, MyMapperStatement> myMapperStatementMap = new HashMap<String, MyMapperStatement>(); //node代表mybatis-config.xml配置文件 <configuration/environment/dataSource>标签下的所有节点数据 Node dataSourceNode = (Node)myXPathParser.evaluate("/configuration/environment/dataSource"); NodeList proertiesNodeList = dataSourceNode.getChildNodes(); for (int i=0; i< proertiesNodeList.getLength(); i++) { Node proertiesNode = proertiesNodeList.item(i); if (proertiesNode.getNodeType() == Node.ELEMENT_NODE) { String name = proertiesNode.getAttributes().getNamedItem("name").getNodeValue(); String value = proertiesNode.getAttributes().getNamedItem("value").getNodeValue(); dataSourceMap.put(name, value); } } //把mybatsi-config.xml数据源配置信息读取出来后,设置到MyEnvironment对象中 MyEnvironment myEnvironment = new MyEnvironment(); myEnvironment.setDriver(dataSourceMap.get("driver")); myEnvironment.setPassword(dataSourceMap.get("password")); myEnvironment.setUrl(dataSourceMap.get("url")); myEnvironment.setUsername(dataSourceMap.get("username")); //node代表mybatis-config.xml配置文件 <configuration/mappers>标签下的所有节点数据 Node mappersNode = (Node)myXPathParser.evaluate("/configuration/mappers"); NodeList mapperNodeList = mappersNode.getChildNodes(); for (int i=0; i< mapperNodeList.getLength(); i++) { Node mapperNode = mapperNodeList.item(i); if (mapperNode.getNodeType() == Node.ELEMENT_NODE) { //xxxMapper.xml文件的路径 String resource = mapperNode.getAttributes().getNamedItem("resource").getNodeValue(); //根据xxxMapper.xml文件的路径读取xxxMapper.xml文件 InputStream inputStream = MyResources.getResourceAsStream(resource); //解析xxxMapper.xml文件,创建新的xpath和document对象 this.myXPathParser = new MyXPathParser(inputStream); Node mapperXmlNode = (Node)myXPathParser.evaluate("/mapper"); //Mapper的命名空间 String namespace = mapperXmlNode.getAttributes().getNamedItem("namespace").getNodeValue(); NodeList mapperXmlNodeList = mapperXmlNode.getChildNodes(); for (int ii=0; ii< mapperXmlNodeList.getLength(); ii++) { Node mapperSQLNode = mapperXmlNodeList.item(ii); if (mapperSQLNode.getNodeType() == Node.ELEMENT_NODE) { String sqlId = mapperSQLNode.getAttributes().getNamedItem("id").getNodeValue(); String parameterType = mapperSQLNode.getAttributes().getNamedItem("parameterType").getNodeValue(); String resultType = mapperSQLNode.getAttributes().getNamedItem("resultType").getNodeValue(); String sql = mapperSQLNode.getTextContent(); //把sql信息读取出来后设置到MyMapperStatement对象 MyMapperStatement myMapperStatement = new MyMapperStatement(); myMapperStatement.setNamespace(namespace); myMapperStatement.setSqlId(sqlId); myMapperStatement.setParameterType(parameterType); myMapperStatement.setResultType(resultType); myMapperStatement.setSql(sql); myMapperStatementMap.put(namespace + "." + sqlId, myMapperStatement); } } } } //把两个xml文件 mybatis-config.xml 和 xxxMapper.xml 的配置信息数据封装到MyConfiguration对象 MyConfiguration myConfiguration = new MyConfiguration(); myConfiguration.setMyEnvironment(myEnvironment); myConfiguration.setMyMapperStatementMap(myMapperStatementMap); return myConfiguration; } } ``` #### 2.2 获取 MySqlSessionFactory,完成第二步流程 + MySqlSessionFactoryBuilder ```java package com.one.mybatis.session; import com.one.mybatis.mapping.MyConfiguration; import com.one.mybatis.parsing.MyXMLConfigBuilder; import java.io.InputStream; public class MySqlSessionFactoryBuilder { // 通过配置文件获取 SqlSessionFactory public MySqlSessionFactory build (InputStream inputStream) { // 通过配置文件初始化 MyXMLConfigBuilder MyXMLConfigBuilder myXMLConfigBuilder = new MyXMLConfigBuilder(inputStream); // 解析配置文件封装成 MyConfiguration MyConfiguration myConfiguration = myXMLConfigBuilder.parse(); // 返回 MySqlSessionFactory return new MySqlSessionFactory(myConfiguration); } } ``` ### 3、获取MySqlSession #### 3.1 解析mapper.xml的sql参数 + SQLTokenParser ```java package com.one.mybatis.parsing; // 解析sql参数 select id from u_user_info whereid = #{id, jdbcType=INTEGER} 改成如下格式,便于jdbc预编译 // select id from u_user_info whereid = ? public class SQLTokenParser { private static final String openToken = "#{"; private static final String closeToken = "}"; public static String parse(String text) { if (text == null || text.isEmpty()) { return ""; } // search open token int start = text.indexOf(openToken); if (start == -1) { return text; } char[] src = text.toCharArray(); int offset = 0; final StringBuilder builder = new StringBuilder(); StringBuilder expression = null; while (start > -1) { if (start > 0 && src[start - 1] == '\\') { // this open token is escaped. remove the backslash and continue. builder.append(src, offset, start - offset - 1).append(openToken); offset = start + openToken.length(); } else { // found open token. let's search close token. 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 { builder.append("?"); offset = end + closeToken.length(); } } start = text.indexOf(openToken, offset); } if (offset < src.length) { builder.append(src, offset, src.length - offset); } return builder.toString(); } public static void main(String[] args) { String sql = "select\n" + " *\n" + " from u_user_info\n" + " where id = #{id, jdbcType=INTEGER} and username = #{username, jdbcType=VARCHAR} and phone = #{phone, jdbcType=VARCHAR}"; System.out.println(SQLTokenParser.parse(sql)); } } ``` #### 3.2 使用反射封装数据库查询到的内容 + Reflection ```java package com.one.mybatis.reflection; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.Date; public class Reflection { //将ResultSet结果集数据设置到Java对象的属性中 public static void setPropertiesToBeanFromResultSet(Object entity, ResultSet resultSet) { //根据结果集,获取到数据库表的元数据信息,这个信息里面有很详细的表的列、字段的信息 try { ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); //字段的个数 int columnCount = resultSetMetaData.getColumnCount(); //获取到userInfo、orderInfo ...对象所声明的成员变量 Field[] fieldArray = entity.getClass().getDeclaredFields(); //循环数据库字段 for (int i=0; i<columnCount; i++) { //数据库表的字段的名字:id,user_name,phone,login_password ........ String columnName = resultSetMetaData.getColumnName(i+1).replace("_", ""); //循环对象的成员变量 for (int ii=0; ii<fieldArray.length; ii++) { //对象中的成员变量的名称,id; userName; phone; loginPassword; String fieldName = fieldArray[ii].getName(); //数据表的字段的名字 和 对象的成员变量的名字,忽略大小写后是否相等 if (columnName.equalsIgnoreCase(fieldName)) { //根据成员变量的名字,拿到代表该成员变量的反射对象Field Field field = entity.getClass().getDeclaredField(fieldName); //允许对该字段进行访问 field.setAccessible(true); //把数据库该字段的字设置到entity对象(userinfo\orderinfo)中 if (field.getType().getSimpleName().equals("Integer")) { //获取数据库字段的值 Integer columnValue = resultSet.getInt(resultSetMetaData.getColumnName(i+1)); //把columnValue这个数据库的值,设置到entity对象的field成员变量中 field.set(entity, columnValue); } else if (field.getType().getSimpleName().equals("Long")) { //获取数据库字段的值 Long columnValue = resultSet.getLong(resultSetMetaData.getColumnName(i+1)); //把columnValue这个数据库的值,设置到entity对象的field成员变量中 field.set(entity, columnValue); } else if (field.getType().getSimpleName().equals("Double")) { //获取数据库字段的值 Double columnValue = resultSet.getDouble(resultSetMetaData.getColumnName(i+1)); //把columnValue这个数据库的值,设置到entity对象的field成员变量中 field.set(entity, columnValue); } else if (field.getType().getSimpleName().equals("String")) { //获取数据库字段的值 String columnValue = resultSet.getString(resultSetMetaData.getColumnName(i+1)); //把columnValue这个数据库的值,设置到entity对象的field成员变量中 field.set(entity, columnValue); } else if (field.getType().getSimpleName().equals("Date")) { //获取数据库字段的值 Date columnValue = resultSet.getDate(resultSetMetaData.getColumnName(i+1)); //把columnValue这个数据库的值,设置到entity对象的field成员变量中 field.set(entity, columnValue); } else if (field.getType().getSimpleName().equals("Boolean")) { //获取数据库字段的值 Boolean columnValue = resultSet.getBoolean(resultSetMetaData.getColumnName(i+1)); //把columnValue这个数据库的值,设置到entity对象的field成员变量中 field.set(entity, columnValue); } } } } } catch (Exception e) { e.printStackTrace(); } } } ``` #### 3.3 获取执行器,使用执行器来获取数据连接并执行数据库操作 + MyDataSourceInterface ```java package com.one.mybatis.pool; import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; // 数据源接口继承java.sql.DataSource ,使用default 设置接口默认方法 public interface MyDataSourceInterface extends DataSource { @Override default Connection getConnection() throws SQLException { return null; } @Override default Connection getConnection(String username, String password) throws SQLException { return null; } @Override default <T> T unwrap(Class<T> iface) throws SQLException { return null; } @Override default boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } @Override default PrintWriter getLogWriter() throws SQLException { return null; } @Override default void setLogWriter(PrintWriter out) throws SQLException { } @Override default void setLoginTimeout(int seconds) throws SQLException { } @Override default int getLoginTimeout() throws SQLException { return 0; } @Override default Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } } ``` + MyDataSource ```java package com.one.mybatis.pool; import com.one.mybatis.mapping.MyEnvironment; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; // 数据源,使用连接池获取、释放连接 public class MyDataSource implements MyDataSourceInterface { private static MyDataSource instance; private MyEnvironment myEnvironment; private Connection connection; private List<Connection> pool; //连接池的大小是10个连接 private static final int POOL_SIZE = 10; private MyDataSource(MyEnvironment myEnvironment) { this.myEnvironment = myEnvironment; this.pool = new ArrayList<>(POOL_SIZE); this.initConnection(); } public static MyDataSource getInstance(MyEnvironment myEnvironment) { if (instance == null) { instance = new MyDataSource(myEnvironment); } return instance; } public void initConnection() { try { Class.forName(myEnvironment.getDriver()); for (int i=0; i<POOL_SIZE; i++) { connection = DriverManager.getConnection(myEnvironment.getUrl(), myEnvironment.getUsername(), myEnvironment.getPassword()); pool.add(connection); } } catch (Exception e) { e.printStackTrace(); } } @Override public synchronized Connection getConnection() throws SQLException { if (pool.size() > 0) { Connection connection = pool.get(0); pool.remove(connection); return connection; } return null; } public synchronized void releaseConnection(Connection connection) { pool.add(connection); } } ``` + MyExecutor ```java package com.one.mybatis.executor; import com.one.mybatis.mapping.MyConfiguration; import com.one.mybatis.mapping.MyMapperStatement; import com.one.mybatis.parsing.SQLTokenParser; import com.one.mybatis.pool.MyDataSource; import com.one.mybatis.reflection.Reflection; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; // 执行器,操作数据库语句 public class MyExecutor { private MyDataSource myDataSource; public MyExecutor(MyConfiguration myConfiguration) { myDataSource = MyDataSource.getInstance(myConfiguration.getMyEnvironment()); } // 查询List结果 public <T> List<T> query(MyMapperStatement myMapperStatement, Object parameter) { //最终返回的结果List List<T> resultList = new ArrayList<T>(); //封装jdbc代码返回结果 ResultSet resultSet = null; Connection connection = null; PreparedStatement preparedStatement = null; try { //从数据库连接池的数据源获取连接 connection = myDataSource.getConnection(); //jdbc预编译sql语句 String sql = SQLTokenParser.parse(myMapperStatement.getSql()); preparedStatement = connection.prepareStatement(sql); //设置参数,多个用for if (parameter instanceof Integer) { preparedStatement.setInt(1, (Integer) parameter); } else if (parameter instanceof Double) { preparedStatement.setDouble(1, (Double) parameter); } else if (parameter instanceof Long) { preparedStatement.setLong(1, (Long) parameter); } else if (parameter instanceof String) { preparedStatement.setString(1, (String) parameter); } //执行操作返回结果 resultSet = preparedStatement.executeQuery(); //将数据库结果resultSet映射到java对象中 handlerResultSet(resultSet, resultList, myMapperStatement.getResultType()); } catch (SQLException e) { e.printStackTrace(); } finally { if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { //关闭连接,此处不是真的关闭连接,而是把连接归还到连接池中 myDataSource.releaseConnection(connection); } } return resultList; } // 对数据库ResultSet结果集进行处理,映射为Java对象 // resultSet jdbc结果集 // resultList 查询到的数据封装成数组 // resultType 查询到的数据要通过反射封装成java对象 @SuppressWarnings("unchecked") public <T> void handlerResultSet(ResultSet resultSet, List<T> resultList, String resultType) { try { Class<T> clazz = (Class<T>)Class.forName(resultType); while (resultSet.next()) { //userInfo 、 orderInfo Object entity = clazz.newInstance(); Reflection.setPropertiesToBeanFromResultSet(entity, resultSet); //把设置好了的java对象放入到最终的结果List中 resultList.add((T)entity); } } catch (Exception e) { e.printStackTrace(); } } } ``` #### 3.4 获取sqlSession,完成第三步流程 + MapperInvocationHandler ```java package com.one.mybatis.proxy; import com.one.mybatis.session.MySqlSession; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MapperInvocationHandler implements InvocationHandler { private MySqlSession mySqlSession; public MapperInvocationHandler(MySqlSession mySqlSession) { this.mySqlSession = mySqlSession; } // method = com.one.mapper.UserInfoMapper.selectByPrimaryKey(id) @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //调用Mapper接口里面的方法对数据库进行操作的时候,就会被该invoke所拦截 String statementKey = method.getDeclaringClass().getTypeName() + "." + method.getName(); //获取参数 Object parameter = args == null ? null : args[0]; //使用mySqlSession.myExecutor查询sql return mySqlSession.selectOne(statementKey, parameter); } } ``` + MySqlSession ```java package com.one.mybatis.session; import com.one.mybatis.executor.MyExecutor; import com.one.mybatis.mapping.MyConfiguration; import com.one.mybatis.mapping.MyMapperStatement; import com.one.mybatis.proxy.MapperInvocationHandler; import java.lang.reflect.Proxy; import java.util.List; public class MySqlSession { private MyConfiguration myConfiguration; private MyExecutor myExecutor; public MySqlSession(MyConfiguration myConfiguration, MyExecutor myExecutor) { this.myConfiguration = myConfiguration; this.myExecutor = myExecutor; } // 泛型方法,获取mapper对应的类 @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> clazz) { //jdk动态代理 MapperInvocationHandler mapperInvocationHandler = new MapperInvocationHandler(this); return (T)Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] {clazz}, mapperInvocationHandler); } //查询单条数据 public <T> T selectOne(String statementKey, Object parameter) { MyMapperStatement myMapperStatement = myConfiguration.getMyMapperStatementMap().get(statementKey); List<T> list = myExecutor.query(myMapperStatement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new RuntimeException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; } } } ``` + MySqlSessionFactory ```java package com.one.mybatis.session; import com.one.mybatis.executor.MyExecutor; import com.one.mybatis.mapping.MyConfiguration; //SqlSession工厂 public class MySqlSessionFactory { private MyConfiguration myConfiguration; public MySqlSessionFactory(MyConfiguration myConfiguration) { this.myConfiguration = myConfiguration; } // 使用执行器获取MySqlSession,完成CRUD public MySqlSession openMySqlSession() { MyExecutor myExecutor = new MyExecutor(myConfiguration); return new MySqlSession(myConfiguration, myExecutor); } } ``` ### 4、MySqlSession 获取 Mapper 接口,完成第四步流程 + 数据库entiry ```java package com.one.model; import java.io.Serializable; import java.util.Date; // 封装数据库返回内容 public class UserInfo implements Serializable { private Integer id; private String userName; private Boolean userType; private Date createTime; 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 Boolean getUserType() { return userType; } public void setUserType(Boolean userType) { this.userType = userType; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } } ``` + 使用session获取mapper接口 ```java UserInfoMapper userInfoMapper = mySqlSession.getMapper(UserInfoMapper.class); ``` ### 5、操作数据库 + 调用Mapper接口完成查询操作,使用反射 ```java UserInfo userInfo = userInfoMapper.selectByPrimaryKey(1); ``` ## 扩展 + Mybatis原生拦截器接口 ``` package org.apache.ibatis.plugin; import java.util.ArrayList; import java.util.Collections; import java.util.List; // Mybatis拦截器,用于框架扩展 public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<>(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } // mybatis 初始化的时候会解析plugin节点调用这个方法 public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); } } ``` + Mybatis扩展入口 ```java //SQL参数扩展 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); //查询结果集扩展 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); //SQL语句扩展 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); //SQL执行器扩展 executor = (Executor) interceptorChain.pluginAll(executor); ``` ## 补充 > 需要自定义线程错误类ErrorTContext,异常类 反射部分需要封装很多元数据信息,需要扩展反射类元数据MetaClass、MetaObject 没有类似于mybatis 的InterceptorChain,无法扩展
上一篇:
other-bash
下一篇:
动态代理
0
赞
93 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册