所需要用到的其他工具或技术:
项目管理工具 : Maven
测试运行工具 : Junit
数据库 : Derby
XML操作工具:Dom4j
继续不废话
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 建表及数据插入(如果在第一节中作过,可以跳过此步):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/freud;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.freud.practice.annotation.UserMapper" /><mapper resource="com/freud/practice/xml/UserMapper.xml" /></mappers></configuration>
User.java对象类(src/main/java/com/freud/practice目录下)package com.freud.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 注解类(src/main/java/com/freud/practice/annotation目录下)package com.freud.practice.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** 标注此注解只能用在方法上 */@Target(ElementType.METHOD)/** 标注此注解生命周期是在Runtime运行时 */@Retention(RetentionPolicy.RUNTIME)public @interface Select{String value();}
UserMapper.java 基于Annotation的配置类(src/main/java/com/freud/practice/annotation目录下)package com.freud.practice.annotation;import com.freud.practice.User;import java.util.List;public interface UserMapper{@Select("select * from USER_TEST_TB")public List<User> getUser();}
Mapper.java 对象类(src/main/java/com/freud/practice/simulation目录下)package com.freud.practice.simulation;/** * * 存储查询结果对象 * */public class Mapper{/** * 返回类型 */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动态代理类(src/main/java/com/freud/practice/simulation目录下)package com.freud.practice.simulation;import com.freud.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{@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{/** * 获得Mapper方法上的Select注解,以此来取得注解中的SQL语句 */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的返回对象类型,此处应当作判断处理,当List的时候,当只返回一个对象的时候. * 为了简单实现功能并与第一节中测试文件不发生冲突起见,此处当作List处理 */String returnType = method.getGenericReturnType().toString();//java.util.List<com.freud.practice.User>if (returnType.startsWith(List.class.getName())){//去掉我们不需要的字符串,得到List中的类型returnType = returnType.replace(List.class.getName(), "").replace("<", "").replace(">", "");}else{// 返回其他对象应当作其他处理,此处为了简单起见,暂不处理}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/freud/practice/simulation目录下)package com.freud.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模拟类(src/main/java/com/freud/practice/simulation目录下)package com.freud.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;}/** * * 通过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("读取配置文件错误!");}finally{configuration.close();}}/** * * 通过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模拟类(src/main/java/com/freud/practice/simulation目录下)package com.freud.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模拟类(src/main/java/com/freud/practice/simulation目录下)package com.freud.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 应该是从配置文件读取,这里简化了 */final String driverString = "org.apache.derby.jdbc.ClientDriver";final String connString = "jdbc:derby://localhost:1527/freud;create=true";try{Class.forName(driverString);/** 获得DB连接 */connection = DriverManager.getConnection(connString);}catch (Exception e){System.out.println("获取DBConnection出错!");}}/** * 基于Annotation的数据库操作 * */@Overridepublic <T> T getMapper(Class<T> clazz){T clazzImpl = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {clazz}, new SQLSelectProxy());return clazzImpl;}/** * * 基于XML的查询操作 */@Overridepublic <E> List<E> selectList(String query) throws Exception{PreparedStatement pstmt = null;ResultSet rs = null;try{/** 简单的PreparedStateme JDBC实现 */pstmt = connection.prepareStatement(xmlSQLs.get(query).getQuerySql());rs = pstmt.executeQuery();/** 执行查询操作 */return executeQuery(rs, xmlSQLs.get(query).getResultType());}finally{if (!rs.isClosed()){rs.close();}if (!pstmt.isClosed()){pstmt.close();}}}/** * * 执行查询操作,并将查询到的结果与配置中的ResultType根据变量名一一对应,通过反射调用Set方法注入各个变量的值 * * @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/freud/practice/xml目录下)<?xml version="1.0" encoding="UTF-8" ?> <!-- namespace 当基于XML进行配置的时候是根据namespace+id来拼接进行SQL操作 --><mapper namespace="com.freud.practice.UserMapper"><!-- select 查询 --><select id="getUser" resultType="com.freud.practice.User">select *from USER_TEST_TB</select></mapper>
TestMyBatis.java 测试类(src/test/java/com/freud/practice目录下)package com.freud.practice;import com.freud.practice.annotation.UserMapper;import com.freud.practice.simulation.SqlSession;import com.freud.practice.simulation.SqlSessionFactory;import com.freud.practice.simulation.SqlSessionFactoryBuilder;import com.freud.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;@Beforepublic void setUp(){source = "test-mybatis-configuration.xml";}/** * * 基于XML格式配置的测试方法 * */@Testpublic void testXMLConfingure(){try{/** * 获得Session */inputStream = TestMyBatis.class.getClassLoader().getResourceAsStream(source);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session = sqlSessionFactory.openSession();/** * 执行Query操作 */List<User> users = (List) session.selectList("com.freud.practice.UserMapper.getUser");System.out.println("Query by XML configuration...");/** * 打印结果 */this.printUsers(users);}catch (Exception e){e.printStackTrace();}}/** * * 基于Annotation配置的测试方法 * */@Testpublic 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();}}@Afterpublic 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的方式对Mybatis实现了一个简单的模拟。旨在理解Mybatis的工作原理。笔者一直觉得当学习一个工具类技术的时候,路线应该是
1.实现一个小例子
2.找材料理解其中原理
3.学习技术细节,并动手全部实现
4.在全部学完之后动手做一个小项目,尽可能的使用这个在技术中的各个环节。
这种方法对笔者来说屡试不爽。
现在我们已经走完了前两步,接下来就是学习技术细节了。