自定義MyBatis

自定義MyBatis是爲了深刻了解MyBatis的原理java

主要的調用是這樣的:mysql

//1.讀取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.建立SqlSessionFactory工廠
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); //3.使用工廠生產SqlSession對象
SqlSession session = factory.openSession(); //4.使用SQLSession建立Dao接口的代理對象
UserDao userDao = session.getMapper(UserDao.class); //5.使用代理對象執行方法
List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } //6.釋放資源
session.close(); in.close();

首先第一步:將配置文件SqlMapConfig.xml轉爲流文件git

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE configuration>

<configuration>
    <!--配置環境-->
    <environments default="mysql">
        <!--配置mysql的環境-->
        <environment id="mysql">
            <!--配置事務類型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置數據源(鏈接池)-->
            <dataSource type="POOLED">
                <!--配置鏈接數據庫的基本信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>

    <!--指定映射配置文件的位置,映射配置文件指的是每一個dao獨立的配置文件-->
    <!--<mappers> <mapper resource="com/jinke/dao/UserDao.xml"/> </mappers>-->
    <!--若是是用註解來配置-->
    <mappers>
        <mapper class="com.jinke.dao.UserDao"/>
    </mappers>
</configuration>
import java.io.InputStream; /*使用類加載器讀取配置文件的類*/
public class Resources { public static InputStream getResourceAsStream(String filePath) { return Resources.class.getClassLoader().getResourceAsStream(filePath); } }

第二步:解析配置文件github

import com.jinke.mybatis.cfg.Configuration; import com.jinke.mybatis.sqlsession.defaults.DefaultSqlSessionFactory; import com.jinke.mybatis.utils.XMLConfigBuilder; import java.io.InputStream; public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream config) { Configuration cfg = XMLConfigBuilder.loadConfiguration(config); return new DefaultSqlSessionFactory(cfg); } }

主要是經過反射將屬性值保存到map中sql

import com.jinke.mybatis.annotations.Select; import com.jinke.mybatis.cfg.Configuration; import com.jinke.mybatis.cfg.Mapper; import com.jinke.mybatis.io.Resources; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import java.util.Map; public class XMLConfigBuilder { public static Configuration loadConfiguration(InputStream config) { Configuration cfg = new Configuration(); try { SAXReader reader = new SAXReader(); Document document = reader.read(config); Element root = document.getRootElement(); List<Element> propertyElements = root.selectNodes("//property"); for (Element propertyElement : propertyElements) { String name = propertyElement.attributeValue("name"); if ("driver".equals(name)) { String driver = propertyElement.attributeValue("value"); cfg.setDriver(driver); } if ("url".equals(name)) { String url = propertyElement.attributeValue("value"); cfg.setUrl(url); } if ("username".equals(name)) { String username = propertyElement.attributeValue("value"); cfg.setUsername(username); } if ("password".equals(name)) { String password = propertyElement.attributeValue("value"); cfg.setPassword(password); } } List<Element> mapperElements = root.selectNodes("//mappers/mapper"); for (Element mapperElement : mapperElements) { Attribute attribute = mapperElement.attribute("resource"); if (attribute != null) { System.out.println("使用的是XML"); String mapperPath = attribute.getValue(); Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath); cfg.setMappers(mappers); } else { System.out.println("使用的是註解"); String daoClassPath = mapperElement.attributeValue("class"); Map<String, Mapper> mappers = loadMapperAnnotation(daoClassPath); cfg.setMappers(mappers); } } return cfg; } catch (Exception e) { e.printStackTrace(); } finally { try { config.close(); } catch (IOException e) { e.printStackTrace(); } } return cfg; } private static Map<String, Mapper> loadMapperConfiguration(String mapperPath) throws IOException { InputStream in = null; Map<String, Mapper> mappers = new HashMap<String, Mapper>(); try { in = Resources.getResourceAsStream(mapperPath); SAXReader reader = new SAXReader(); Document document = reader.read(in); Element root = document.getRootElement(); String namespace = root.attributeValue("namespace"); List<Element> selectElements = root.selectNodes("//select"); for (Element selectElement : selectElements) { String id = selectElement.attributeValue("id"); String resultType = selectElement.attributeValue("resultType"); String queryString = selectElement.getText(); String key = namespace + "." + id; Mapper mapper = new Mapper(); mapper.setQueryString(queryString); mapper.setResultType(resultType); mappers.put(key, mapper); } return mappers; } catch (Exception e) { e.printStackTrace(); } return mappers; } private static Map<String, Mapper> loadMapperAnnotation(String daoClassPath) throws Exception { Map<String, Mapper> mappers = new HashMap<String, Mapper>(); Class daoClass = Class.forName(daoClassPath); Method[] methods = daoClass.getMethods(); for (Method method : methods) { boolean isAnnotated = method.isAnnotationPresent(Select.class); if (isAnnotated) { Mapper mapper = new Mapper(); Select selectAnno = method.getAnnotation(Select.class); String queryString = selectAnno.value(); mapper.setQueryString(queryString); Type type = method.getGenericReturnType(); if (type instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) type; Type[] types = ptype.getActualTypeArguments(); Class domainClass = (Class) types[0]; String resultType = domainClass.getName(); mapper.setResultType(resultType); } String methodName = method.getName(); String className = method.getDeclaringClass().getName(); String key = className + "." + methodName; mappers.put(key, mapper); } } return mappers; } }

第三步:DefaultSqlSessionFactory工廠生產出DefaultSqlSession對象數據庫

import com.jinke.mybatis.cfg.Configuration; import com.jinke.mybatis.sqlsession.SqlSession; import com.jinke.mybatis.sqlsession.SqlSessionFactory; public class DefaultSqlSessionFactory implements SqlSessionFactory { private Configuration cfg; public DefaultSqlSessionFactory(Configuration cfg) { this.cfg = cfg; } public SqlSession openSession() { return new DefaultSqlSession(cfg); } }

第四步:DefaultSqlSession執行動態代理微信

import com.jinke.mybatis.cfg.Configuration; import com.jinke.mybatis.sqlsession.SqlSession; import com.jinke.mybatis.sqlsession.proxy.MapperProxy; import com.jinke.mybatis.utils.DataSourceUtil; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; public class DefaultSqlSession implements SqlSession { private Configuration cfg; private Connection connection; public DefaultSqlSession(Configuration cfg) { this.cfg = cfg; this.connection = DataSourceUtil.getConnection(cfg); } public <T> T getMapper(Class<T> daoInterfaceClass) { return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(), new Class[]{daoInterfaceClass}, new MapperProxy(cfg.getMappers(), connection)); } public void close() { if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

執行sql語句session

import com.jinke.mybatis.cfg.Mapper; import com.jinke.mybatis.utils.Executor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.sql.Connection; import java.util.Map; public class MapperProxy implements InvocationHandler { private Map<String, Mapper> mappers; private Connection connection; public MapperProxy(Map<String, Mapper> mappers, Connection connection) { this.mappers = mappers; this.connection = connection; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); String className = method.getDeclaringClass().getName(); String key = className + "." + methodName; Mapper mapper = mappers.get(key); if (mapper == null) { throw new IllegalArgumentException("傳入的參數有誤"); } return new Executor().selectList(mapper, connection); } }
import com.jinke.mybatis.cfg.Mapper; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.sql.*; import java.util.ArrayList; import java.util.List; public class Executor { public <E> List<E> selectList(Mapper mapper, Connection conn) { PreparedStatement pstm = null; ResultSet rs = null; try { String queryString = mapper.getQueryString(); String resultType = mapper.getResultType(); Class domainClass = Class.forName(resultType); pstm = conn.prepareStatement(queryString); rs = pstm.executeQuery(); List<E> list = new ArrayList<E>(); while (rs.next()) { E obj = (E) domainClass.newInstance(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); for (int i = 1; i < columnCount; i++) { String columnName = rsmd.getColumnName(i); Object columnValue = rs.getObject(columnName); PropertyDescriptor pd = new PropertyDescriptor(columnName, domainClass); Method writeMethod = pd.getWriteMethod(); writeMethod.invoke(obj, columnValue); } list.add(obj); } return list; } catch (Exception e) { e.printStackTrace(); } finally { release(pstm, rs); } return null; } private void release(PreparedStatement pstm, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (pstm != null) { try { pstm.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

最後放一張文件結構圖mybatis

代碼地址 https://github.com/king1039/MyBatisapp

歡迎關注個人微信公衆號:安卓圈

相關文章
相關標籤/搜索