模擬Mybatis的實(shí)現(xiàn)方法
所需要用到的其他工具或技術(shù):
項(xiàng)目管理工具 : Maven
測(cè)試運(yùn)行工具 : Junit
數(shù)據(jù)庫(kù) : Derby
XML操作工具:Dom4j
繼續(xù)不廢話
Maven Dependencies:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derby</artifactId> <version>10.10.2.0</version> </dependency> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derbyclient</artifactId> <version>10.10.2.0</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency>
SQL 建表及數(shù)據(jù)插入(如果在第一節(jié)中作過(guò),可以跳過(guò)此步):
CREATE TABLE USER_TEST_TB( ID INT PRIMARY KEY, USERNAME VARCHAR(20) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, NICKNAME VARCHAR(20) NOT NULL ); INSERT INTO USER_TEST_TB VALUES(1,'1st','111','Jack'); INSERT INTO USER_TEST_TB VALUES(2,'2nd','222','Rose'); INSERT INTO USER_TEST_TB VALUES(3,'3rd','333','Will');
Mybatis配置文件 src/main/resource源目錄下
test-mybatis-configuration.xml <?xml version="1.0" encoding="UTF-8" ?> <configuration> <properties> <property name="driver" value="org.apache.derby.jdbc.ClientDriver" /> <property name="url" value="jdbc:derby://localhost:1527/bjpowernode;create=true" /> </properties> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${driver}" /> <property name="url" value="${url}" /> </dataSource> </environment> </environments> <mappers> <mapper class="com.bjpowernode.practice.annotation.UserMapper" /> <mapper resource="com/bjpowernode/practice/xml/UserMapper.xml" /> </mappers> </configuration>
User.java對(duì)象類(lèi)(src/main/java/com/bjpowernode/practice目錄下)
package com.bjpowernode.practice; /** * * User Model * */ public class User { private String id; private String username; private String password; private String nickname; public String getId() { return id; } public void setId(String id) { this.id = id; } 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; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } }
Select.java 注解類(lèi)(src/main/java/com/bjpowernode/practice/annotation目錄下)
package com.bjpowernode.practice.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** 標(biāo)注此注解只能用在方法上 */ @Target(ElementType.METHOD) /** 標(biāo)注此注解生命周期是在Runtime運(yùn)行時(shí) */ @Retention(RetentionPolicy.RUNTIME) public @interface Select { String value(); }
UserMapper.java 基于Annotation的配置類(lèi)(src/main/java/com/bjpowernode/practice/annotation目錄下)
package com.bjpowernode.practice.annotation; import com.bjpowernode.practice.User; import java.util.List; public interface UserMapper { @Select("select * from USER_TEST_TB") public List<User> getUser(); }
Mapper.java 對(duì)象類(lèi)(src/main/java/com/bjpowernode/practice/simulation目錄下)
package com.bjpowernode.practice.simulation; /** * * 存儲(chǔ)查詢結(jié)果對(duì)象 * */ public class Mapper { /** * 返回類(lèi)型 */ private String resultType; /** * 查詢SQL */ private String querySql; public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } public String getQuerySql() { return querySql; } public void setQuerySql(String querySql) { this.querySql = querySql; } }
SQLSelectProxy.java AOP動(dòng)態(tài)代理類(lèi)(src/main/java/com/bjpowernode/practice/simulation目錄下)
package com.bjpowernode.practice.simulation; import com.bjpowernode.practice.annotation.Select; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; public class SQLSelectProxy implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** * 獲得Mapper方法上的Select注解,以此來(lái)取得注解中的SQL語(yǔ)句 */ Select select = method.getAnnotation(Select.class); if (!method.isAnnotationPresent(Select.class)) { throw new RuntimeException("缺少@Select注解!"); } PreparedStatement pstmt = null; ResultSet rs = null; Object obj = null; try { pstmt = SqlSessionImpl.connection.prepareStatement(select.value()); rs = pstmt.executeQuery(); /** * 獲得Method的返回對(duì)象類(lèi)型,此處應(yīng)當(dāng)作判斷處理,當(dāng)List的時(shí)候,當(dāng)只返回一個(gè)對(duì)象的時(shí)候. * 為了簡(jiǎn)單實(shí)現(xiàn)功能并與第一節(jié)中測(cè)試文件不發(fā)生沖突起見(jiàn),此處當(dāng)作List處理 */ String returnType = method.getGenericReturnType().toString();//java.util.List<com.bjpowernode.practice.User> if (returnType.startsWith(List.class.getName())) { //去掉我們不需要的字符串,得到List中的類(lèi)型 returnType = returnType.replace(List.class.getName(), "").replace("<", "").replace(">", ""); } else { // 返回其他對(duì)象應(yīng)當(dāng)作其他處理,此處為了簡(jiǎn)單起見(jiàn),暫不處理 } obj = SqlSessionImpl.executeQuery(rs, returnType); } finally { if (rs != null && !rs.isClosed()) { rs.close(); } if (pstmt != null && !pstmt.isClosed()) { pstmt.close(); } } return obj; } }
SqlSession.java Mybatis模擬接口(src/main/java/com/bjpowernode/practice/simulation目錄下)
package com.bjpowernode.practice.simulation; import java.util.List; /** * * 模擬SqlSession * */ public interface SqlSession { public <T> T getMapper(Class<T> clazz); public <E> List<E> selectList(String query) throws Exception; }
SqlSessionFactory.java Mybatis模擬類(lèi)(src/main/java/com/bjpowernode/practice/simulation目錄下)
package com.bjpowernode.practice.simulation; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * * 模擬SqlSessionFactory * */ public class SqlSessionFactory { private InputStream configuration; public SqlSession openSession() throws IOException { SqlSessionImpl session = new SqlSessionImpl(); loadConfigurations(session); return session; } /** * * 通過(guò)Dom4j讀取配置文件信息 * * @param session * @throws IOException */ private void loadConfigurations(final SqlSessionImpl session) throws IOException { try { Document document = new SAXReader().read(configuration); Element root = document.getRootElement(); List<Element> mappers = root.element("mappers").elements("mapper"); for (Element mapper : mappers) { if (mapper.attribute("resource") != null) { session.setXmlSQLs(loadXMLConfiguration(mapper.attribute("resource").getText())); } if (mapper.attribute("class") != null) { } } } catch (Exception e) { System.out.println("讀取配置文件錯(cuò)誤!"); } finally { configuration.close(); } } /** * * 通過(guò)dom4j讀取Mapper.xml中的信息 * * @param resource * @return * @throws DocumentException * @throws IOException */ private Map<String, Mapper> loadXMLConfiguration(String resource) throws DocumentException, IOException { Map<String, Mapper> map = new HashMap<String, Mapper>(); InputStream is = null; try { is = this.getClass().getClassLoader().getResourceAsStream(resource); Document document = new SAXReader().read(is); Element root = document.getRootElement(); if (root.getName().equalsIgnoreCase("mapper")) { String namespace = root.attribute("namespace").getText(); for (Element select : (List<Element>) root.elements("select")) { Mapper mapperModel = new Mapper(); mapperModel.setResultType(select.attribute("resultType").getText()); mapperModel.setQuerySql(select.getText().trim()); map.put(namespace + "." + select.attribute("id").getText(), mapperModel); } } } finally { is.close(); } return map; } public InputStream getConfiguration() { return configuration; } public void setConfiguration(InputStream configuration) { this.configuration = configuration; } }
SqlSessionFactoryBuilder.java Mybatis模擬類(lèi)(src/main/java/com/bjpowernode/practice/simulation目錄下)
package com.bjpowernode.practice.simulation; import java.io.InputStream; /** * * 模擬SqlSessionFactoryBuilder * */ public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream is) { SqlSessionFactory sessionFactory = new SqlSessionFactory(); sessionFactory.setConfiguration(is); return sessionFactory; } }
SqlSessionImpl.java Mybatis模擬類(lèi)(src/main/java/com/bjpowernode/practice/simulation目錄下)
package com.bjpowernode.practice.simulation; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * * 模擬SqlSessionImpl * */ public class SqlSessionImpl implements SqlSession { /** DB connection */ public static Connection connection; private Map<String, Mapper> xmlSQLs; private List<String> annotationClasses; public SqlSessionImpl() { /** * driverString 和 connString 應(yīng)該是從配置文件讀取,這里簡(jiǎn)化了 */ final String driverString = "org.apache.derby.jdbc.ClientDriver"; final String connString = "jdbc:derby://localhost:1527/bjpowernode;create=true"; try { Class.forName(driverString); /** 獲得DB連接 */ connection = DriverManager.getConnection(connString); } catch (Exception e) { System.out.println("獲取DBConnection出錯(cuò)!"); } } /** * 基于Annotation的數(shù)據(jù)庫(kù)操作 * */ @Override public <T> T getMapper(Class<T> clazz) { T clazzImpl = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {clazz}, new SQLSelectProxy()); return clazzImpl; } /** * * 基于XML的查詢操作 */ @Override public <E> List<E> selectList(String query) throws Exception { PreparedStatement pstmt = null; ResultSet rs = null; try { /** 簡(jiǎn)單的PreparedStateme JDBC實(shí)現(xiàn) */ pstmt = connection.prepareStatement(xmlSQLs.get(query).getQuerySql()); rs = pstmt.executeQuery(); /** 執(zhí)行查詢操作 */ return executeQuery(rs, xmlSQLs.get(query).getResultType()); } finally { if (!rs.isClosed()) { rs.close(); } if (!pstmt.isClosed()) { pstmt.close(); } } } /** * * 執(zhí)行查詢操作,并將查詢到的結(jié)果與配置中的ResultType根據(jù)變量名一一對(duì)應(yīng),通過(guò)反射調(diào)用Set方法注入各個(gè)變量的值 * * @param rs * @param type * @return * @throws Exception */ public static <E> List<E> executeQuery(ResultSet rs, String type) throws Exception { int count = rs.getMetaData().getColumnCount(); List<String> columnNames = new ArrayList<String>(); for (int i = 1; i <= count; i++) { columnNames.add(rs.getMetaData().getColumnName(i)); } final List list = new ArrayList<Object>(); while (rs.next()) { Class modelClazz = Class.forName(type); Object obj = modelClazz.newInstance(); for (Method setMethods : modelClazz.getMethods()) { for (String columnName : columnNames) { if (setMethods.getName().equalsIgnoreCase("set" + columnName)) { setMethods.invoke(obj, rs.getString(columnName)); } } } list.add(obj); } return list; } public Map<String, Mapper> getXmlSQLs() { return xmlSQLs; } public void setXmlSQLs(Map<String, Mapper> xmlSQLs) { this.xmlSQLs = xmlSQLs; } public List<String> getAnnotationClasses() { return annotationClasses; } public void setAnnotationClasses(List<String> annotationClasses) { this.annotationClasses = annotationClasses; } }
UserMapper.xml 基于XML的Mapper配置文件(src/main/java/com/bjpowernode/practice/xml目錄下)
<?xml version="1.0" encoding="UTF-8" ?> <!-- namespace 當(dāng)基于XML進(jìn)行配置的時(shí)候是根據(jù)namespace+id來(lái)拼接進(jìn)行SQL操作 --> <mapper namespace="com.bjpowernode.practice.UserMapper"> <!-- select 查詢 --> <select id="getUser" resultType="com.bjpowernode.practice.User"> select * from USER_TEST_TB </select> </mapper>
TestMyBatis.java 測(cè)試類(lèi)(src/test/java/com/bjpowernode/practice目錄下)
package com.bjpowernode.practice; import com.bjpowernode.practice.annotation.UserMapper; import com.bjpowernode.practice.simulation.SqlSession; import com.bjpowernode.practice.simulation.SqlSessionFactory; import com.bjpowernode.practice.simulation.SqlSessionFactoryBuilder; import com.bjpowernode.practice.simulation.SqlSessionImpl; import java.io.InputStream; import java.sql.SQLException; import java.text.MessageFormat; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TestMyBatis { /** 配置置文件 */ private String source; private InputStream inputStream; private SqlSessionFactory sqlSessionFactory; @Before public void setUp() { source = "test-mybatis-configuration.xml"; } /** * * 基于XML格式配置的測(cè)試方法 * */ @Test public void testXMLConfingure() { try { /** * 獲得Session */ inputStream = TestMyBatis.class.getClassLoader().getResourceAsStream(source); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); /** * 執(zhí)行Query操作 */ List<User> users = (List) session.selectList("com.bjpowernode.practice.UserMapper.getUser"); System.out.println("Query by XML configuration..."); /** * 打印結(jié)果 */ this.printUsers(users); } catch (Exception e) { e.printStackTrace(); } } /** * * 基于Annotation配置的測(cè)試方法 * */ @Test public void testAnnotationConfingure() { try { inputStream = TestMyBatis.class.getClassLoader().getResourceAsStream(source); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); UserMapper userMapper = session.getMapper(UserMapper.class); System.out.println("\r\nQuery by annotation configuration..."); this.printUsers(userMapper.getUser()); } catch (Exception e) { e.printStackTrace(); } } @After public void clearUp() throws SQLException { if (SqlSessionImpl.connection != null && !SqlSessionImpl.connection.isClosed()) { SqlSessionImpl.connection.close(); } } private void printUsers(final List<User> users) { int count = 0; for (User user : users) { System.out.println(MessageFormat.format("==User[{0}]=================", ++count)); System.out.println("User Id: " + user.getId()); System.out.println("User UserName: " + user.getUsername()); System.out.println("User Password: " + user.getPassword()); System.out.println("User nickname: " + user.getNickname()); } } }
以上就是基于XML以及Annotation的方式對(duì)Mybatis實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的模擬。旨在理解Mybatis的工作原理。
筆者一直覺(jué)得當(dāng)學(xué)習(xí)一個(gè)工具類(lèi)技術(shù)的時(shí)候,路線應(yīng)該是
1.實(shí)現(xiàn)一個(gè)小例子
2.找材料理解其中原理
3.學(xué)習(xí)技術(shù)細(xì)節(jié),并動(dòng)手全部實(shí)現(xiàn)
4.在全部學(xué)完之后動(dòng)手做一個(gè)小項(xiàng)目,盡可能的使用這個(gè)在技術(shù)中的各個(gè)環(huán)節(jié)。
總結(jié)
以上所述是小編給大家介紹的模擬Mybatis的實(shí)現(xiàn)方法,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
詳解java接口(interface)在不同JDK版本中的變化
這篇文章主要介紹了詳解java接口(interface)在不同JDK版本中的變化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02Java橋接模式實(shí)例詳解【簡(jiǎn)單版與升級(jí)版】
這篇文章主要介紹了Java橋接模式,結(jié)合實(shí)例形式分析了java橋接模式簡(jiǎn)單版與升級(jí)版兩種實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-07-07SpringCloud之Feign代理,聲明式服務(wù)調(diào)用方式
這篇文章主要介紹了SpringCloud之Feign代理,聲明式服務(wù)調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03SpringMvc+Mybatis+Pagehelper分頁(yè)詳解
這篇文章主要介紹了SpringMvc+Mybatis+Pagehelper分頁(yè)詳解,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下的相關(guān)資料2017-01-01SSM項(xiàng)目中使用攔截器和過(guò)濾器的實(shí)現(xiàn)示例
這篇文章主要介紹了SSM項(xiàng)目中使用攔截器和過(guò)濾器的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04springboot框架中如何整合mybatis框架思路詳解
這篇文章主要介紹了springboot框架中如何整合mybatis框架,本文通過(guò)示例圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12關(guān)于json序列化(javaBean轉(zhuǎn)Json的細(xì)節(jié)處理)
這篇文章主要介紹了關(guān)于json序列化(javaBean轉(zhuǎn)Json的細(xì)節(jié)處理),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2022-03-03SpringBoot+SpringSecurity+jwt實(shí)現(xiàn)驗(yàn)證
本文主要介紹了SpringBoot+SpringSecurity+jwt實(shí)現(xiàn)驗(yàn)證,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07