模拟Mybatis的实现方法

时间:2022-09-23 10:53:28

所需要用到的其他工具或技术:

项目管理工具 : Maven

测试运行工具 : Junit

数据库 : Derby

XML操作工具:Dom4j

继续不废话

Maven Dependencies:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<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 建表及数据插入(如果在第一节中作过,可以跳过此步):

?
1
2
3
4
5
6
7
8
9
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源目录下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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对象类(src/main/java/com/bjpowernode/practice目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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 注解类(src/main/java/com/bjpowernode/practice/annotation目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
/** 标注此注解只能用在方法上 */
@Target(ElementType.METHOD)
/** 标注此注解生命周期是在Runtime运行时 */
@Retention(RetentionPolicy.RUNTIME)
public @interface Select
{
 String value();
}

UserMapper.java 基于Annotation的配置类(src/main/java/com/bjpowernode/practice/annotation目录下)

?
1
2
3
4
5
6
7
8
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 对象类(src/main/java/com/bjpowernode/practice/simulation目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.bjpowernode.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/bjpowernode/practice/simulation目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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注解,以此来取得注解中的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.bjpowernode.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/bjpowernode/practice/simulation目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
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模拟类(src/main/java/com/bjpowernode/practice/simulation目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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;
 }
 /**
 *
 * 通过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/bjpowernode/practice/simulation目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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模拟类(src/main/java/com/bjpowernode/practice/simulation目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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 应该是从配置文件读取,这里简化了
 */
 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出错!");
 }
 }
 /**
 * 基于Annotation的数据库操作
 *
 */
 @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
 {
 /** 简单的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/bjpowernode/practice/xml目录下)

?
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" ?>
 <!-- namespace 当基于XML进行配置的时候是根据namespace+id来拼接进行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 测试类(src/test/java/com/bjpowernode/practice目录下)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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格式配置的测试方法
 *
 */
 @Test
 public 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.bjpowernode.practice.UserMapper.getUser");
 System.out.println("Query by XML configuration...");
 /**
 * 打印结果
 */
 this.printUsers(users);
 }
 catch (Exception e)
 {
 e.printStackTrace();
 }
 }
 /**
 *
 * 基于Annotation配置的测试方法
 *
 */
 @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的方式对Mybatis实现了一个简单的模拟。旨在理解Mybatis的工作原理。

笔者一直觉得当学习一个工具类技术的时候,路线应该是

1.实现一个小例子

2.找材料理解其中原理

3.学习技术细节,并动手全部实现

4.在全部学完之后动手做一个小项目,尽可能的使用这个在技术中的各个环节。

总结

以上所述是小编给大家介绍的模拟Mybatis的实现方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!

原文链接:http://blog.sina.com.cn/s/blog_9c6852670102wvhh.html