当前位置:网站首页 / MyBatis / 正文

模拟Mybatis的实现方法

2022年11月24日 | 来源 : 千自学 | 分类 : MyBatis

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

项目管理工具 : 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/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目录下)

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目录下)

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目录下)

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目录下)

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目录下)

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目录下)

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目录下)

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目录下)

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目录下)

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目录下)

<?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目录下)

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的实现方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


标签:MyBatis

猜你喜欢

相关发表
最近发表
标签列表