引⼊相关依赖35pom.xml
<?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>org.god.ibatis</groupId> <artifactId>course7</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <!--依赖--> <dependencies> <!--依赖dom4j--> <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.1.3</version> </dependency> <!--依赖jaxen--> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.2.0</version> </dependency> <!--依赖junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!--mysql驱动依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> </dependencies> <properties> <!-- jdk版本用于编译代码--> <maven.compiler.source>1.8</maven.compiler.source> <!-- 使用jdk版本的操作程序--> <maven.compiler.target>1.8</maven.compiler.target> </properties></project>
1.创建基本类35代码的第一步是org.god.ibatis.工具类utilsresources专门完成“类路径”中资源的加载。package org.god.ibatis.utils;import java.io.InputStream;/** * godbatis框架提供的工具类。 35 * 该工具专门完成了“类路径”中资源的加载。 */public class Resources { /** * 建议将工具的结构方法私有化。 * 因为工具中的方法是静态的,不需要创建对象就可以调用。 * 所有结构方法都是私有化的,以避免new对象。 * 这只是一种编程习惯。 */ private Resources(){} /** * 从类路径中加载资源。 * @param resource 资源文件放置在类路径中。 * @return 指向资源文件的输入流。 */ public static InputStream getResourceAsStream(String resource){ return ClassLoader.getSystemClassLoader().getResourceAsStream(resource); }}
2.第二步分析SqlSessionFactory应有的属性362.1MappedStatement36普通java类。POJO,SQL标签封装。36
MappedStatement对象对应SQL标签。
SQL标签中的所有信息都包装在MapedStatement对象中。
代码在org.god.ibatis.coreMappedStatementpackage org.god.ibatis.core;/** * 普通java类。POJO,SQL标签封装。 36 * MappedStatement对象对应SQL标签。 * SQL标签中的所有信息都包装在MapedStatement对象中。 * 面向对象的编程思想。 */public class MappedStatement { /** * sql语句 */ private String sql; /** * 要封装的结果集类型。有时候resulttype是null。 * 比如:insert delete 在update语句中,resulttype是null。 * 只有当sql语句是select语句时,resulttype才值得。 */ private String resultType; @Override public String toString() { return "MappedStatement{" + "sql='" + sql + '\'' + ", resultType='" + resultType + '\'' + '}'; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } public MappedStatement(String sql, String resultType) { this.sql = sql; this.resultType = resultType; } public MappedStatement() { }}
2.1分析SqlSessionFactoryBuilder类35-3636提供⼀个⽆参数构造⽅法,再提供⼀build⽅法,build⽅返回SqlSessionFactory对象
package org.god.ibatis.core;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.Node;import org.dom4j.io.SAXReader;import org.god.ibatis.utils.Resources;import javax.sql.DataSource;import java.io.InputStream;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/** * SqlSessionFactory构建器对象。 * 通过SqlSessionFactorybuilderbuild方法进行分析 * godbatis-config.xml文件,然后创建SqlSessionFactory对象。 */public class SqlSessionFactoryBuilder { /** * 无参数结构方法。 */ public SqlSessionFactoryBuilder(){} /** * godbatis分析-config.构建SqlSessionFactory对象的xml文件。 * @param in 指向godbatis指向godbatis-config.xml文件的输入流。 * @param in 指向godbatis指向godbatis-config.xml文件的输入流。 * @return SqlSessionFactory对象。 */ public SqlSessionFactory build(InputStream in){ SqlSessionFactory factory = null; try { // godbatis分析-config.xml文件 SAXReader reader = new SAXReader(); Document document = reader.read(in); Element environments = (Element) document.selectSingleNode("/configuration/environments"); String defaultId = environments.attributeValue("default"); Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultId + "']"); Element transactionElt = environment.element("transactionManager"); Element dataSourceElt = environment.element("dataSource"); List<String> sqlMapperXMLPathList = new ArrayList<>(); // 在整个配置文件中获取所有mapper标签 List<Node> nodes = document.selectNodes("//mapper"); nodes.forEach(node -> { Element mapper = (Element) node; String resource = mapper.attributeValue("resource"); sqlMapperXMLPathList.add(resource); }); // 获取数据源对象 DataSource dataSource = getDataSource(dataSourceElt); // 获取事务管理器 Transaction transaction = getTransaction(transactionElt,dataSource); // mappedstatements获取mapped Map<String, MappedStatement> mappedStatements = getMappedStatements(sqlMapperXMLPathList); // 分析完成后,构建SqlSessionFactory对象。 factory = new SqlSessionFactory(transaction, mappedStatements); } catch (Exception e) { e.printStackTrace(); } return factory; } /** * 所有Sqlmaper分析.xml文件,然后构建Map集合。 43 * @param sqlMapperXMLPathList * @return */ private Map<String, MappedStatement> getMappedStatements(List<String> sqlMapperXMLPathList) { Map<String, MappedStatement> mappedStatements = new HashMap<>(); sqlMapperXMLPathList.forEach(sqlMapperXMLPath -> { try { SAXReader reader = new SAXReader(); Document document = reader.read(Resources.getResourceAsStream(sqlMapperXMLPath)); Element mapper = (Element) document.selectSingleNode("mapper"); String namespace = mapper.attributeValue("namespace"); List<Element> elements = mapper.elements(); elements.forEach(element -> { String id = element.attributeValue("id"); // namespace和id拼接在这里,生成最终的sqlid String sqlId = namespace + "." + id; String resultType = element.attributeValue("resultType"); String sql = element.getTextTrim(); MappedStatement mappedStatement = new MappedStatement(sql, resultType); mappedStatements.put(sqlId, mappedStatement); }); } catch (Exception e) { e.printStackTrace(); } }); return mappedStatements; } /** * 获取事务管理器 42 * @param transactionElt 事务管理器的标签元素 * @param dataSource 数据源对象 * @return */ private Transaction getTransaction(Element transactionElt, DataSource dataSource) { Transaction transaction = null; String type = transactionElt.attributeValue("type").trim().toUpperCase(); if (Const.JDBC_TRANSACTION.equals(type)) { transaction = new JdbcTransaction(dataSource, false); // 默认情况下,事务将开始,将来需要手动提交。 } if (Const.MANAGED_TRANSACTION.equals(type)) { transaction = new ManagedTransaction(); } return transaction; } /** * 获取数据源对象 41 * @param dataSourceElt 数据源标签元素 * @return */ private DataSource getDataSource(Element dataSourceElt) { Map<String,String> map = new HashMap<>(); // 获得所有property List<Element> propertyElts = dataSourceElt.elements("property"); propertyElts.forEach(propertyElt -> { String name = propertyElt.attributeValue("name"); String value = propertyElt.attributeValue("value"); map.put(name, value); }); DataSource dataSource = null; String type = dataSourceElt.attributeValue("type").trim().toUpperCase(); if (Const.UN_POOLED_DATASOURCE.equals(type)) { dataSource = new UnPooledDataSource(map.get("driver"), map.get("url"), map.get("username"), map.get("password")); } if (Const.POOLED_DATASOURCE.equals(type)) { dataSource = new PooledDataSource(); } if (Const.JNDI_DATASOURCE.equals(type)) { dataSource = new JNDIDataSource(); } return dataSource; }}
2.2分析SqlSessionFactory36-37代码.god.ibatis.core37SqlSessionFactory
package org.god.ibatis.core;import java.util.Map;/** * SqlSessionFactory对象: 35 * SqlSessionFactory对应一个数据库。 * SqlSesssionFactory对象可以通过Sqlsesion获得。(开会) * SqlSessionFactory对象可以打开多个SqlSession会话。 */public class SqlSessionFactory { /** * 事务管理器属性 37 * 可灵活切换事务管理器。 * SqlSessionFactory类中的事务管理器应面向接口编程。 * SqlSessionFactory类应该有一个事务管理器接口。 */ private Transaction transaction; /** * Map集合存储sql语句。 36 * key是sqlId * value是相应的SQL标签信息对象。 */ private Map mappedStatements; public Transaction getTransaction() { return transaction; } public void setTransaction(Transaction transaction) { this.transaction = transaction; } public Map getMappedStatements() { return mappedStatements; } public void setMappedStatements(Map mappedStatements) { this.mappedStatements = mappedStatements; } /** * 获取SQL会话对象。 45 * @return */ public SqlSession openSession(){ // 打开会话的前提是打开连接。(连接打开) transaction.openConnection(); // 创建SqlSession对象 SqlSession sqlSession = new SqlSession(this); return sqlSession; } public SqlSessionFactory(Transaction transaction, Map mappedStatements) { this.transaction = transaction; this.mappedStatements = mappedStatements; } public SqlSessionFactory() { }}
3.第三步提取事务管理器接口373.1transation事务管理器接口。事务管理器接口。
所有事务管理器都应遵循这一规范。
这个界面应该由JDBC事务管理器和MANAGED事务管理器实现。
Transaction事务管理器:提供管理事务的方法。
代码在org.god.ibatis.core37,40transactionpackage org.god.ibatis.core;import java.sql.Connection;/** * 事务管理器接口。 37 * 所有事务管理器都应遵循这一规范。 * 这个界面应该由JDBC事务管理器和MANAGED事务管理器实现。 * Transaction事务管理器:提供管理事务的方法。 */public interface Transaction { /** * 提交事务 */ void commit(); /** * 回滚事务 */ void rollback(); /** * 关闭事务 */ void close(); /** * 真正打开数据库连接。 */ void openConnection(); /** * 获取数据库连接对象。 */ void openConnection(); /** * 获取数据库连接对象。 */ Connection getConnection();}
4.事务管理器实现384.1分析Jdbctransaction实现transation接口38代码.god.ibatis.coreJdbctransaction.2Managedtransaction实现transation接口38代码.god.ibatis.coreManagedTransactionpackage org.god.ibatis.core;import java.sql.Connection;/** * MANAGED事务管理器。(godbatis不再实现这一类。) 38 */public class ManagedTransaction implements Transaction{ @Override public void commit() { } @Override public void rollback() { } @Override public void close() { } @Override public void openConnection() { } @Override public Connection getConnection() { return null; }}
5.第五步数据源实现395.1UnPoleddataSource实现类39代码.god.ibatis.coreUnPooledDataSourcepackage org.god.ibatis.core;import java.io.PrintWriter;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;/** * 实现数据源:UNPOOLED (重点实现这种方式。) 38 * Conection对象每次都不使用连接池。 */public class UnPooledDataSource implements javax.sql.DataSource{ private String url; private String username; private String password; /** * 创建数据源对象。 * @param driver * @param url * @param username * @param password */ public UnPooledDataSource(String driver, String url, String username, String password) { try { // 直接注册驱动 Class.forName(driver); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } this.url = url; this.username = username; this.password = password; } @Override public Connection getConnection() throws SQLException { Connection connection = DriverManager.getConnection(url, username, password); return connection; } @Override public Connection getConnection(String username, String password) throws SQLException { return null; } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { } @Override public void setLoginTimeout(int seconds) throws SQLException { } @Override public int getLoginTimeout() throws SQLException { return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } @Override public T unwrap(Class iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class iface) throws SQLException { return false; }}
5.2PoleddataSource实现类39代码.god.ibatis.corePooledDataSourcepackage org.god.ibatis.core;import java.io.PrintWriter;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;/** * 实现数据源:POOLED 38 * Conection对象采用godbatis框架内置的数据库连接池获取。(这一点没有实现。) */public class PooledDataSource implements javax.sql.DataSource{ @Override public Connection getConnection() throws SQLException { // Conection对象从数据库连接池中获取。(这个数据库连接池包装在我的godbatis框架内。) return null; } @Override public Connection getConnection(String username, String password) throws SQLException { return null; } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { } @Override public void setLoginTimeout(int seconds) throws SQLException { } @Override public int getLoginTimeout() throws SQLException { return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } @Override public T unwrap(Class iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class iface) throws SQLException { return false; }}
5.3JNDIDataSource实现类39代码.god.ibatis.core JNDIDataSourcepackage org.god.ibatis.core;import java.io.PrintWriter;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;/** * 实现数据源:JNDI 38 * 使用第三方数据库连接池获取Conection对象。(这一点没有实现。) */public class JNDIDataSource implements javax.sql.DataSource{ @Override public Connection getConnection() throws SQLException { return null; } @Override public Connection getConnection(String username, String password) throws SQLException { return null; } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { } @Override public void setLoginTimeout(int seconds) throws SQLException { } @Override public int getLoginTimeout() throws SQLException { return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } @Override public T unwrap(Class iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class iface) throws SQLException { return false; }}
6.事务管理器改造第六步400步代码在org.god.ibatis.core40JdbcTransactionpackage org.god.ibatis.core;import javax.sql.DataSource;import java.sql.Connection;import java.sql.SQLException;/** * JDBC事务管理器(godbatis框架目前只实现JDBCtransaction。) 38 */public class JdbcTransaction implements Transaction{ /** * 数据源属性 * 经典设计:面向接口编程。 */ private DataSource dataSource; /** * 自动提交标志 * true表示自动提交 * false表示不使用自动提交 */ private boolean autoCommit; /** * 连接对象 */ private Connection connection; @Override public Connection getConnection() { return connection; } /** * 创建事务管理器对象 * @param dataSource * @param autoCommit */ public JdbcTransaction(DataSource dataSource, boolean autoCommit) { this.dataSource = dataSource; this.autoCommit = autoCommit; } @Override public void commit() { try { connection.commit(); } catch (SQLException e) { e.printStackTrace(); } } @Override public void rollback() { try { connection.rollback(); } catch (SQLException e) { e.printStackTrace(); } } @Override public void close() { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } @Override public void openConnection(){ if (connection == null) { try { connection = dataSource.getConnection(); // 开启事务 connection.setAutoCommit(autoCommit); } catch (SQLException e) { e.printStackTrace(); } } }}
7.第七步获取数据源对象41代码.god.ibatis.core41是SqlsessionFactorybuilder中的方法getdatasourcer/** * 获取数据源对象 * @param dataSourceElt 数据源标签元素 * @return */ private DataSource getDataSource(Element dataSourceElt) { Map map = new HashMap<>(); // 获得所有property List propertyElts = dataSourceElt.elements("property"); propertyElts.forEach(propertyElt -> { String name = propertyElt.attributeValue("name"); String value = propertyElt.attributeValue("value"); map.put(name, value); }); DataSource dataSource = null; String type = dataSourceElt.attributeValue("type").trim().toUpperCase(); if (Const.UN_POOLED_DATASOURCE.equals(type)) { dataSource = new UnPooledDataSource(map.get("driver"), map.get("url"), map.get("username"), map.get("password")); } if (Const.POOLED_DATASOURCE.equals(type)) { dataSource = new PooledDataSource(); } if (Const.JNDI_DATASOURCE.equals(type)) { dataSource = new JNDIDataSource(); } return dataSource; }
8.第八步是获取事务管理器对象的42代码.god.ibatis.core42是SqlsessionFactorybuilder中的方法gettransaction** * 获取事务管理器 * @param transactionElt 事务管理器的标签元素 * @param dataSource 数据源对象 * @return */ private Transaction getTransaction(Element transactionElt, DataSource dataSource) { Transaction transaction = null; String type = transactionElt.attributeValue("type").trim().toUpperCase(); if (Const.JDBC_TRANSACTION.equals(type)) { transaction = new JdbcTransaction(dataSource, false); // 默认情况下,事务将开始,将来需要手动提交。 } if (Const.MANAGED_TRANSACTION.equals(type)) { transaction = new ManagedTransaction(); } return transaction; }
9.SQLMap在org中集合43代码,第九步获取存储.god.ibatis.core43是SqlsessionFactorybuilder中的方法。/** * 所有Sqlmaper分析.然后构建Map集合的xml文件。 * @param sqlMapperXMLPathList * @return */ private Map getMappedStatements(List sqlMapperXMLPathList) { Map mappedStatements = new HashMap<>(); sqlMapperXMLPathList.forEach(sqlMapperXMLPath -> { try { SAXReader reader = new SAXReader(); Document document = reader.read(Resources.getResourceAsStream(sqlMapperXMLPath)); Element mapper = (Element) document.selectSingleNode("mapper"); String namespace = mapper.attributeValue("namespace"); List elements = mapper.elements(); elements.forEach(element -> { String id = element.attributeValue("id"); // namespace和id拼接在这里,生成最终的sqlid String sqlId = namespace + "." + id; String resultType = element.attributeValue("resultType"); String sql = element.getTextTrim(); MappedStatement mappedStatement = new MappedStatement(sql, resultType); mappedStatements.put(sqlId, mappedStatement); }); } catch (Exception e) { e.printStackTrace(); } }); return mappedStatements; }
10.第十步测试框架44代码org.god.ibatis.test44是GodBatistestest中的方法testsssionFactory@Test public void testSqlSessionFactory(){ SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("godbatis-config.xml")); System.out.println(sqlSessionFactory); }
11.第十一十二十三步45-47包装SqlSession对象45代码.god.ibatis.core46-47Sqlsessionpackage org.god.ibatis.core;import java.lang.reflect.Method;import java.sql.*;/** * SQL语句的会话对象专门负责SQL语句的执行 45 */public class SqlSession { private SqlSessionFactory factory; public SqlSession(SqlSessionFactory factory) { this.factory = factory; } /** * 执行insert语句,将记录插入数据库表中。 46 * @param sqlId sql语句的id * @param pojo 插入的数据。 * @return */ public int insert(String sqlId, Object pojo){ int count = 0; try { // JDBC代码,执行insert语句,完成插入操作。 Connection connection = factory.getTransaction().getConnection(); // insert into t_user values(#{id},#{name},#{age}) String godbatisSql = factory.getMappedStatements().get(sqlId).getSql(); // insert into t_user(id,name,age) values(?,?,?) String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$*}, "?"); PreparedStatement ps = connection.prepareStatement(sql); // 给?占位符传值 47 // 难度是什么: // 第一:你不知道有多少人? // 第二:你不知道在pojo对象中赋予哪个属性 ? // ps.String(几个问号, 传什么值); // 这里都是setstring,所以数据库表中的字段类型要求都是varchar。这是godbatis失败的地方。 int fromIndex = 0; int index = 1; while(true){ int jingIndex = godbatisSql.indexOf("#", fromIndex); if (jingIndex < 0) { break; } int youKuoHaoIndex = godbatisSql.indexOf("}", fromIndex); String propertyName = godbatisSql.substring(jingIndex + 2, youKuoHaoIndex).trim(); fromIndex = youKuoHaoIndex + 1; // 有属性名id,如何获得id的属性值?调用getid()方法 //拼方法名 String getMethodName = "get" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1); ///使用反射机制根据方法名获得方法对象 Method getMethod = pojo.getClass().getDeclaredMethod(getMethodName); ///利用反射机制根据调用获得的方法获得属性值 Object propertyValue = getMethod.invoke(pojo); //给?赋值 ps.setString(index, propertyValue.toString()); index++; } // 执行SQL语句 count = ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } return count; } /** * 执行查询语句,返回一个对象。该方法仅适用于返回记录的sql语句。 49 * @param sqlId * @param param * @return */ public Object selectOne(String sqlId, Object param){ Object obj = null; try { Connection connection = factory.getTransaction().getConnection(); MappedStatement mappedStatement = factory.getMappedStatements().get(sqlId); // 这是DQL查询的句子 // select * from t_user where id = #{id} String godbatisSql = mappedStatement.getSql(); String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$*}, "?"); PreparedStatement ps = connection.prepareStatement(sql); // 给占位符传值 ps.setString(1, param.toString()); // 查询返回结果集 ResultSet rs = ps.executeQuery(); // 包装的结果类型。 String resultType = mappedStatement.getResultType(); // org.god.ibatis.pojo.User // 从结果中集中取数据,java对象的封装 if (rs.next()) { //使用反射 // Classss获取resulttype Class resultTypeClass = Class.forName(resultType); // 调用无参数构建方法创建对象 obj = resultTypeClass.newInstance(); // Object obj = new User(); // 给User类id,name,age属性赋值 // 赋予obj对象哪个属性哪个值。 50 /* mysql> select * from t_user where id = '1111'; +------+----------+------+ | id | name | age | +------+----------+------+ | 1111 | zhangsan | 20 | +------+----------+------+ 解决问题的关键:将查询结果的列名作为属性名。 列名是id,所以属性名是:id 排名是name,所以属性名是:name */ ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); for (int i = 0; i < columnCount; i++) { //getcolumnname String propertyName = rsmd.getColumnName(i + 1); // 拼接方法名 String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1); // 获取set方法对象 Method setMethod = resultTypeClass.getDeclaredMethod(setMethodName, String.class); // 使用set方法给对象obj属性赋值 setMethod.invoke(obj, rs.getString(propertyName)); } } } catch (Exception e) { e.printStackTrace(); } return obj; } // 局部测试 47 public static void main(String[] args) { String sql = "insert into t_user values(#{id},#{name},#{age})"; int fromIndex = 0; int index = 1; while(true){ int jingIndex = sql.indexOf("#", fromIndex); if (jingIndex < 0) { break; } System.out.println(index); index++; int youKuoHaoIndex = sql.indexOf("}", fromIndex); String propertyName = sql.substring(jingIndex + 2, youKuoHaoIndex).trim(); System.out.println(propertyName); fromIndex = youKuoHaoIndex + 1; } } /** * 提交事务 46 */ public void commit(){ factory.getTransaction().commit(); } /** * 回滚事务 46 */ public void rollback(){ factory.getTransaction().rollback(); } /** * 关闭事务 46 */ public void close(){ factory.getTransaction().close(); }}
insert方法实现46动态占位符传值47代码.god.ibatis.core46-47是SqlSession中的insertt/** * 执行insert语句,将记录插入数据库表。 46 * @param sqlId sql语句的id * @param pojo 插入的数据。 * @return */ public int insert(String sqlId, Object pojo){ int count = 0; try { // JDBC代码,执行insert语句,完成插入操作。 Connection connection = factory.getTransaction().getConnection(); // insert into t_user values(#{id},#{name},#{age}) String godbatisSql = factory.getMappedStatements().get(sqlId).getSql(); // insert into t_user(id,name,age) values(?,?,?,?) String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$*}, "?"); PreparedStatement ps = connection.prepareStatement(sql); // 给?占位符传值 47 // 难度是什么: // 第一:你不知道有多少人? // 第二:你不知道在pojo对象中赋予哪个属性 ? // ps.String(几个问号, 传什么值); // 这里都是setstring,所以数据库表中的字段类型要求都是varchar。这是godbatis失败的地方。 int fromIndex = 0; int index = 1; while(true){ int jingIndex = godbatisSql.indexOf("#", fromIndex); if (jingIndex < 0) { break; } int youKuoHaoIndex = godbatisSql.indexOf("}", fromIndex); String propertyName = godbatisSql.substring(jingIndex + 2, youKuoHaoIndex).trim(); fromIndex = youKuoHaoIndex + 1; // 有属性名id,如何获得id的属性值?调用getid()方法 //拼方法名 String getMethodName = "get" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1); ///使用反射机制根据方法名获得方法 Method getMethod = pojo.getClass().getDeclaredMethod(getMethodName); ///利用反射机制根据调用获得的方法获得属性值 Object propertyValue = getMethod.invoke(pojo); //给?赋值 ps.setString(index, propertyValue.toString()); index++; } // 执行SQL语句 count = ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } return count; }
org局部测试代码.god.ibatis.core47是SqlSession中的main// 局部测试 47 public static void main(String[] args) { String sql = "insert into t_user values(#{id},#{name},#{age})"; int fromIndex = 0; int index = 1; while(true){ int jingIndex = sql.indexOf("#", fromIndex); if (jingIndex < 0) { break; } System.out.println(index); index++; int youKuoHaoIndex = sql.indexOf("}", fromIndex); String propertyName = sql.substring(jingIndex + 2, youKuoHaoIndex).trim(); System.out.println(propertyName); fromIndex = youKuoHaoIndex + 1; } }
12.第14步测试插入数据48代码.god.ibatis.test48是GodBatistestest中的方法testinsertuser//测试插入数据 48 @Test public void testInsertUser(){ SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("godbatis-config.xml")); SqlSession sqlSession = sqlSessionFactory.openSession(); // 执行SQL insert User user = new User("1111", "zhangsan", "20"); int count = sqlSession.insert("user.insertUser", user); System.out.println(count); sqlSession.commit(); sqlSession.close(); }
13.第十五步selectone方法在orgg中实现49-50代码.god.ibatis.test49-50是GodBatistest中的方法selectone/** * 执行查询句,返回一个对象。该方法仅适用于返回记录的sql语句。 49 * @param sqlId * @param param * @return */ public Object selectOne(String sqlId, Object param){ Object obj = null; try { Connection connection = factory.getTransaction().getConnection(); MappedStatement mappedStatement = factory.getMappedStatements().get(sqlId); // 这是DQL查询的句子 // select * from t_user where id = #{id} String godbatisSql = mappedStatement.getSql(); String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$*}, "?"); PreparedStatement ps = connection.prepareStatement(sql); // 给占位符传值 ps.setString(1, param.toString()); // 查询返回结果集 ResultSet rs = ps.executeQuery(); // 包装的结果类型。 String resultType = mappedStatement.getResultType(); // org.god.ibatis.pojo.User // 从结果中集中取数据,java对象的封装 if (rs.next()) { //使用反射 // 获取resulttypeClass Class resultTypeClass = Class.forName(resultType); // 调用无参数构建方法创建对象 obj = resultTypeClass.newInstance(); // Object obj = new User(); // 给User类id,name,age属性赋值 // 赋予obj对象哪个属性哪个值。 50 /* mysql> select * from t_user where id = '1111'; +------+----------+------+ | id | name | age | +------+----------+------+ | 1111 | zhangsan | 20 | +------+----------+------+ 解决问题的关键:将查询结果的列名作为属性名。 列名是id,所以属性名是:id 排名是name,所以属性名是:name */ ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); for (int i = 0; i < columnCount; i++) { //getcolumnname String propertyName = rsmd.getColumnName(i + 1); // 拼接方法名 String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1); // 获取set方法对象 Method setMethod = resultTypeClass.getDeclaredMethod(setMethodName, String.class); // 使用set方法给对象obj属性赋值 setMethod.invoke(obj, rs.getString(propertyName)); } } } catch (Exception e) { e.printStackTrace(); } return obj; }
14.第十七步最终版测试51代码在org.god.ibatis.test51是GodBatistestest中的方法testelectonet///测试查询一个数据 51 @Test public void testSelectOne(){ SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("godbatis-config.xml")); SqlSession sqlSession = sqlSessionFactory.openSession(); // 执行SQL语句 Object obj = sqlSession.selectOne("user.selectById", "1111"); System.out.println(obj); sqlSession.close(); }