mybatis的简单模拟
程序员文章站
2024-02-27 19:17:51
...
我们首先来观察 mybatis-config.xml,里面需要配置一个数据源,那么我们准备一个数据源的实体类。
mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
<property name="username" value="scott"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/me/mybatis/DeptMapper.xml"/>
</mappers>
</configuration>
数据源的实体类:
对于mapper的映射文件,我们也可以提取成一个实体类,我们观察mapper.xml发现,我们可以从中把 sql语句、参数类型、返回值类型、是否为更新的sql语句 组成一个实体类。
DeptMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.me.bean.DeptMapper">
<insert id="addDept" parameterType="com.me.bean.DeptBean">
insert into dept values(#{deptno},#{dname},#{loc})
</insert>
<insert id="addDept1" parameterType="map">
insert into dept values(#{deptno},#{dname},#{loc})
</insert>
<select id="findAll" resultType="com.me.bean.DeptBean">
select deptno,dname,loc from dept
</select>
</mapper>
mapper实体类:
我们查询oracle中的dept表作为测试,创建一个dept的实体类
接下来具体实现。
先看看解析mybatis-config.xml的MyBatisConfig.java。使用dom4j解析xml。这个类主要解析了 数据源信息、mapper映射路径信息。
public class MyBatisConfig {
private DataSource dataSource; //数据源实体
private List<String> mappers=new ArrayList<String>(); //保存mapper映射文件
public MyBatisConfig(String config) {
try {
readXml(config);
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 解析mybatis-config.xml
* @param config //文件路径
* @throws DocumentException
*/
private void readXml(String config) throws DocumentException {
SAXReader reader = new SAXReader();
Document doc = reader.read(this.getClass().getClassLoader().getResourceAsStream(config));
XPath xPath = doc.createXPath("//dataSource/property"); //解析mybatis-config.xml下的property所有节点
List<Element> properties = xPath.selectNodes(doc);
dataSource=new DataSource();//创建一个数据源实体
String pname=null;
for (Element e1:properties) { //获取所有的property节点信息,保存到数据源实体类中
pname=e1.attributeValue("name");
switch (pname) {
case "driver":
dataSource.setDriver(e1.attributeValue("value"));
break;
case "url":
dataSource.setUrl(e1.attributeValue("value"));
break;
case "username":
dataSource.setUsername(e1.attributeValue("value"));
break;
case "password":
dataSource.setPassword(e1.attributeValue("value"));
break;
default:
break;
}
}
//System.out.println(dataSource);
//开始获取mybatis-config.xml中的<mappers>节点,即映射文件
xPath = doc.createXPath("//mappers/mapper");
List<Element> list = xPath.selectNodes(doc);
for (Element el:list) {
mappers.add(el.attributeValue("resource")); //保存到一个集合中
}
//到这里我们已经解析完了mybatis-config.xml
//System.out.println(mappers);
}
public DataSource getDataSource() {
return dataSource;
}
public List<String> getMappers() {
return mappers;
}
public static void main(String[] args) {
MyBatisConfig myBatisConfig = new MyBatisConfig("mybatis-config.xml");
}
}
模拟SqlSessionFactory的SqlSessionFactory.java。这个类解析了mapper映射文件,并将解析出来的信息保存到实体类MapperInfo中。
public class SqlSessionFactory {
private MyBatisConfig config; //mybatis-config.xml的解析结果
private Map<String, MapperInfo> mapperInfos =new HashMap<String, MapperInfo>();//mapper映射文件的解析结果
public SqlSessionFactory(String configXml) {
config=new MyBatisConfig(configXml); //解析mybatis-config.xml
try {
parseXml(); //解析mapper映射文件
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 解析mapper映射文件
* @throws DocumentException
*/
private void parseXml() throws DocumentException {
List<String> mappers = config.getMappers(); //获取映射文件路径
if (mappers!=null && !mappers.isEmpty()) {
SAXReader reader = new SAXReader();
Document doc= null;
XPath xPath = null;
List<Element> ops=null;
String opname=null;
MapperInfo mapperInfo=null;
//循环解析所有映射文件
for (String mapper:mappers) {
doc = reader.read(this.getClass().getClassLoader().getResourceAsStream(mapper));
xPath=doc.createXPath("//mapper/*"); //获取DeptMapper.xml下mapper下的所有节点
ops=xPath.selectNodes(doc);
for (Element el:ops) {
mapperInfo=new MapperInfo(); //创建一个mapper实体类
opname=el.getName();
//如果是select节点,说明不是更新语句
if (opname.equals("select")) {
mapperInfo.setUpdate(false);
}
//将各项信息填入新创建的mapper实体类
mapperInfo.setParameterType(el.attributeValue("parameterType"));
mapperInfo.setResultType(el.attributeValue("resultType"));
mapperInfo.setSql(el.getTextTrim());
//保存结果到一个集合中
mapperInfos.put(el.attributeValue("id"), mapperInfo);
}
}
}
}
public MyBatisConfig getConfig() {
return config;
}
public Map<String, MapperInfo> getMapperInfos() {
return mapperInfos;
}
}
最后就是比较难实现的SqlSession。这个类反射机制用的比较多,代码有详细注释。
public class SqlSession {
private SqlSessionFactory factory; //SqlSessionFactory
private DBHelper db;
public SqlSession(SqlSessionFactory factory){
this.factory=factory;
db=new DBHelper(factory.getConfig().getDataSource()); //用已经解析好的数据源实体类去初始化DBhelper
}
/**
* 查询
* @param sqlId mapper映射文件下的节点id
* @param params 参数
* @return
*/
public <T> List<T> selectList(String sqlId,Object ... params) {
String sql;//要执行的sql语句
Class C; //反射的对象
try {
MapperInfo mapperInfo=factory.getMapperInfos().get(sqlId);//获取该sqlId节点下的所有信息
if (mapperInfo==null) {
return null;
}
sql=mapperInfo.getSql(); //获取sql语句
if (mapperInfo.isUpdate()) {
return null;
}
String className = mapperInfo.getResultType();//获取返回值类型
C=Class.forName(className); //反射得到该返回值类型的class
return db.findObject(sql, C,params); //调用dbhelper按对象查询
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 更新情况(update,insert,delete)
* @param sqlId
* @param obj
* @return
*/
public int update(String sqlId,Object obj) {
int result=0; //执行结果
MapperInfo mapperInfo = factory.getMapperInfos().get(sqlId);
if (mapperInfo==null) {
return 0;
}
String sql=mapperInfo.getSql();
String className = mapperInfo.getParameterType();
Pattern pattern = Pattern.compile("[#][{]\\w+}"); //正则表达式 。将#{xxxx}变成#{?}
Matcher matcher = pattern.matcher(sql);
ArrayList<String> colNames = new ArrayList<String>();
while (matcher.find()) {
colNames.add(matcher.group().replaceAll("[#{}]*", ""));
}
sql=matcher.replaceAll("?");
//如果参数类型为map
if ("map".equals(className)) {
Map<String, Object> map = (Map<String, Object>) obj;
List<Object> params=new ArrayList<Object>(); //参数集合
for (String colName:colNames) {
params.add(map.get(colName));
}
return db.update(sql, params);//使用DBhelper查询
}
if ("String".equals(className)) {
return -1;
}
//参数类型为对象时
try {
Class c =Class.forName(className); //反射得到class
Method[] methods=c.getDeclaredMethods(); //获取该类中的声明方法
List<Method> getMethods = new ArrayList<Method>(); //保存该对象的get方法
for (Method md:methods) {
if (md.getName().startsWith("get")) {
getMethods.add(md); //如果方法为get方法,保存
}
}
List<Object> params=new ArrayList<Object>();//参数集合
String mname;
for (String colName:colNames) {
for(Method md:getMethods){
mname="get"+colName.substring(0,1).toUpperCase()+colName.substring(1);//转换为getXxx的形式,例如getdeptno转换为getDeptno
if (mname.equals(md.getName())) { //用get方法或的参数
params.add(md.invoke(obj)); //反向执行
}
}
}
return db.update(sql, params); //返回执行结果
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
测试类:
public class _Main {
@Test
public void test() {
SqlSessionFactory sqFactory = new SqlSessionFactory("mybatis-config.xml");
//System.out.println(sqFactory.getMapperInfos());
SqlSession session = new SqlSession(sqFactory);
List<Object> dept = session.selectList("findAll");
System.out.println(dept);
}
@Test
public void test2() {
SqlSessionFactory sqFactory = new SqlSessionFactory("mybatis-config.xml");
SqlSession session = new SqlSession(sqFactory);
int result=session.update("addDept", new DeptBean(88,"hah","666"));
System.out.println(result);
}
@Test
public void test3() {
SqlSessionFactory sqFactory = new SqlSessionFactory("mybatis-config.xml");
SqlSession session = new SqlSession(sqFactory);
Map<String, Object> map = new HashMap<String, Object>();
map.put("deptno", 99);
map.put("dname", "呵呵");
map.put("loc", "888");
int result=session.update("addDept1", map);
System.out.println(result);
}
}
DBHelper部分代码;
public class DBHelper {
private DataSource dataSource;
public DBHelper(DataSource dataSource) {
this.dataSource=dataSource;
try {
Class.forName(dataSource.getDriver());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
* @return
*/
public Connection getConnection() {
Connection con = null;
try {
con = DriverManager.getConnection(dataSource.getUrl(),dataSource.getUsername(),dataSource.getPassword());
//Context context = new InitialContext();
//DataSource ds = (DataSource) context.lookup("java:comp/env/mysql");
// con = ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
}