自定義MyBatis框架,實現一些簡單的基礎功能
阿新 • • 發佈:2020-10-04
git程式碼地址:https://gitee.com/lv_guoliang/mybatis
實現思路:
使用端:
提供核心配置檔案
sqlMapConfig.xml: 存放資料來源資訊, 引入mapper.xml
Mapper.xml: sql語句的配置檔案資訊
框架端:
1. 讀取配置檔案
讀取完以後以流的形式存在,我們不能將讀取到的配置資訊以流的形式存放在記憶體中,建立javaBean來儲存
(1) Configuration: 存放資料庫基本資訊、Map<唯一標識, Mapper>: namespace+"."+id
(2)MappedSatement: sql語句、statement型別、輸入引數java型別、輸出引數java型別
2.解析配置檔案
建立SqlSessionFactoryBuilder類:
方法:SqlSessionFactoryBuild():
第一:使用dom4j解析配置檔案,將解析出來的內容封裝到Configuration和MappedStatement中
第二:建立SqlSessionFactory的實現類DefaultSQLSession
3.建立SqlSessionFactory
方法:openSession(): 獲取sqlSession介面的實現類例項物件
4.建立SqlSession介面及實現類:主要封裝CRUD方法
核心程式碼
測試類呼叫方法
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream); ISqlSession sqlSession = sqlSessionFactory.openSession(); userDao = sqlSession.getMapper(IUserDao.class);
User user = new User();
user.setId(4);
user.setUsername("gxy");
user.setPassword("111111");
user.setBirthday("1990-01-01");
int result = userDao.addUser(user);
System.out.println("新增sql成功:" + result);
獲取配置檔案,構建Configuration物件
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream in) throws PropertyVetoException, DocumentException { //第一;使用dom4j解析配置檔案, 將解析出來的內容封裝到Configuration中 XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(); Configuration configuration = xmlConfigBuilder.parseConfig(in); //第二: 建立sql IDefaultSqlSessionFactory defautlSqlSessionFactory = new IDefaultSqlSessionFactory(configuration); return defautlSqlSessionFactory; } }
獲取代理物件,生成查詢,更新方法
public class DefaultSqlSession implements ISqlSession { private Configuration configuration; public DefaultSqlSession(Configuration configuration) { this.configuration = configuration; } @Override public <T> T getMapper(Class<?> mapperClass) { //使用JDK動態代理來為Dao介面生成代理物件,並返回 Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[] {mapperClass }, new InvocationHandler() { //底層還是去執行JDBC程式碼 //準備引數1.statementId @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); String className = method.getDeclaringClass().getName(); String statementId = className + "." + methodName; Type genericReturnType = method.getGenericReturnType(); /* 返回型別是否是集合 */ if (genericReturnType instanceof ParameterizedType) { List<Object> objects = selectList(statementId, args); return objects; } else { Class<?> returnType = method.getReturnType(); String type = returnType.getTypeName(); if (type.equals("int")) { return update(statementId, args); } else { Object object = selectOne(statementId, args); return object; } } } }); return (T) proxyInstance; } @Override public <E> List<E> selectList(String statementId, Object... params) throws InvocationTargetException, SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, IntrospectionException, ClassNotFoundException { //將要去完成對simpleExcutor裡的query方法的呼叫 simpleExecutor simpleExecutor = new simpleExecutor(); MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId); List<Object> queryList = simpleExecutor.query(configuration, mappedStatement, params); return (List<E>) queryList; } @Override public <T> T selectOne(String statementId, Object... params) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, IntrospectionException, ClassNotFoundException { List<Object> objects = selectList(statementId, params); if (objects.size() == 1) { return (T) objects.get(0); } else { throw new RuntimeException("查詢結果為空或者返回結果更多"); } } @Override public int update(String statementId, Object... params) throws ClassNotFoundException, SQLException, NoSuchFieldException, IllegalAccessException { simpleExecutor simpleExecutor = new simpleExecutor(); MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId); return simpleExecutor.update(configuration, mappedStatement, params); } }
具體實現更新,查詢方法
public class simpleExecutor implements IExecutor { private PreparedStatement getParamsPreparedStatement(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { // 1. 註冊驅動,獲取連結 Connection connection = configuration.getDataSource().getConnection(); // 2. 獲取sql語句 String sql = mappedStatement.getSql(); BoundSql boundSql = getBoundSql(sql); // 3.獲取預處理物件 PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText()); // 4. 設定引數 String paramterType = mappedStatement.getParamterType(); Class<?> paramtertypeClass = getClassType(paramterType); List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList(); for (int i = 0; i < parameterMappingList.size(); i++) { ParameterMapping parameterMapping = parameterMappingList.get(i); String content = parameterMapping.getContent(); //反射 Field declaredField = paramtertypeClass.getDeclaredField(content); //暴力訪問 declaredField.setAccessible(true); Object o = declaredField.get(params[0]); preparedStatement.setObject(i + 1, o); } return preparedStatement; } @Override public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException, IntrospectionException, ClassNotFoundException { PreparedStatement preparedStatement = this.getParamsPreparedStatement(configuration, mappedStatement, params); // 5. 執行sql ResultSet resultSet = preparedStatement.executeQuery(); String resultType = mappedStatement.getResultType(); Class<?> resultTypeClass = getClassType(resultType); ArrayList<Object> objects = new ArrayList<>(); // 6. 封裝返回結果集 while (resultSet.next()) { Object o = resultTypeClass.newInstance(); //元資料 ResultSetMetaData metaData = resultSet.getMetaData(); for (int i = 1; i < metaData.getColumnCount() ; i++) { //獲取欄位名 String columnName = metaData.getColumnName(i); Object value = resultSet.getObject(columnName); //使用反射或者內省,根據資料庫表和實體的對應關係,完成封裝 PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass); Method writeMethod = propertyDescriptor.getWriteMethod(); writeMethod.invoke(o, value); } objects.add(o); } return (List<E>) objects; } @Override public int update(Configuration configuration, MappedStatement mappedStatement, Object... params) throws ClassNotFoundException, SQLException, IllegalAccessException, NoSuchFieldException { PreparedStatement preparedStatement = this.getParamsPreparedStatement(configuration, mappedStatement, params); //執行sql int i = preparedStatement.executeUpdate(); return i; } private Class<?> getClassType(String paramterType) throws ClassNotFoundException { if (paramterType != null) { Class<?> aClass = Class.forName(paramterType); return aClass; } return null; } /** * 完成#{}的解析工作 1,將#{}解析成?, 2.解析出#{}的值進行儲存 * @param sql * @return */ private BoundSql getBoundSql(String sql) { //標記處理類:配置標記解析器來完成對佔位符的解析處理工作 ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler(); GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler); //解析出來的sql String parseSql = genericTokenParser.parse(sql); //#{}解析出來的引數名稱 List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings(); BoundSql boundSql = new BoundSql(parseSql, parameterMappings); return boundSql; } }