手写mybatis框架
我们首先要了解JDBC和mybtis的执行流程
- JDBC的执行流程:
/**
- JDBC的执行流程,mybatis其实就是对ibatis的封装,而ibatis的底层封装了JDBC的操作
*/
public static void main(String[] args) {
try {
// 1.加载JDBC的驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.通过驱动管理器获取connection
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
// 3.通过Connection获取statement
Statement statement = connection.createStatement();
// 4.statement相当于数据库操作对象,statement去执行sql语句
statement.execute("select * from user;");
// 5.获取数据库返回的结果集
ResultSet resultSet = statement.getResultSet();
}catch (Exception e){
e.printStackTrace();
}
}
- mybatis的执行流程:
/**
* mybatis的执行流程:
* 1.根据配置文件myBatis-config.xml加载初始化参数
* 2.通过configuration去build一个SqlSessionFactory
* 3.获取一个SqlSession
* 4.最后通过SqlSession获取一个接口的代理类,去执行SQL语句,返回执行结果
*/
public class TestMain {
private SqlSession session;
@Before
public void before() throws Exception{
InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(new InputStreamReader(is));
session = sessionFactory.openSession();
}
@After
public void after(){
try {
session.close();
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void test(){
DeptDao dao = session.getMapper(DeptDao.class);
List list = dao.findAll();
System.out.println(list.get(0));
System.out.println(list.get(1));
}
}
手写mybtis框架
加载配置文件,这里我们只加载mapper中的sql语句;我们根据DAO中的方法和XML的SQL标签的ID一一对应;
提供一个根据方法名去获取DAO方法对应的SQL和该SQL执行后返回的数据类型;
public class Configuration {
private Connection connection;
private String path;
private String daoName;
private Map sqlMap = new HashMap();
private Map typeMap = new HashMap();
public Map getSqlMap() {
return sqlMap;
}
public Map getTypeMap() {
return typeMap;
}
public synchronized Connection getConnection() {
return connection;
}
public Configuration(String path){
this.path = path;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
}catch (Exception e){
e.printStackTrace();
}
initConfiguration();
}
public void close(){
try {
if (connection != null){
connection.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 初始化配置文件,此处我们只做简单的处理mapper文件,
* 其它:
* plugins(配置拦截器)
* properties(一般存放数据链接和账号密码信息)
* environment(读取properties信息生成数据链接对象)
* transactionManager(事务管理,一般交给spring管理)
* dataSource:
* dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。数据源类型有三种:UNPOOLED,POOLED,JNDI。
* UNPOOLED是没有数据库连接池的,没执行一次操作,打开一次数据库,关闭一次数据库.效率较为低下
* POOLED是存在数据库连接池的,没有操作数据库从数据库连接池中拿取连接
* JNDI这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
* mappers:它是数据库mapper.xml
*
*/
public void initConfiguration() {
try {
//1.读取资源文件夹mapper,该文件夹的XML和DAO一一对应,XML中的sql标签的id和DAO中的方法一一对应,读取mapper文件是使用了dom4j技术
URL url = Configuration.class.getClassLoader().getResource("");
File fileFolder = new File(url.getPath() + path);
String[] fileStrs = fileFolder.list();
for (int i=0;fileStrs != null && fileStrs.length > i;i++){
String fullDaoName = fileStrs[i];
daoName = fullDaoName.substring(0,fullDaoName.indexOf("."));
InputStream in = Resources.getResourceAsStream(path + "/" + fullDaoName);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new InputStreamReader(in));
setSqlMapAndTypeMethod(document.getRootElement(),(String) null);
}
//System.out.println(sqlMap);
//System.out.println(typeMap);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 利用递归遍历xml文件,获取dao对应xml文件的sql和返回值类型
*
* @param rootElement
* @param methodName
*/
public void setSqlMapAndTypeMethod(Element rootElement,String methodName){
List<Attribute> attributeList = rootElement.attributes();
for (int i=0;attributeList != null && attributeList.size()>0 && attributeList.size() > i;i++){
Attribute attribute = attributeList.get(i);
String name = attribute.getName();
String value = attribute.getValue();
if("id".equals(name)){
methodName = daoName + "." +value;
sqlMap.put(methodName,rootElement.getStringValue());
} else if ("resultType".equals(name)){
typeMap.put(methodName,value);
}
}
List<Element> elementList = rootElement.elements();
for (int i=0;elementList != null && elementList.size() > 0 && elementList.size() > i;i++){
Element element = elementList.get(i);
setSqlMapAndTypeMethod(element,methodName);
}
}
public static void main(String[] args) {
Configuration configuration = new Configuration("mapper");
configuration.initConfiguration();
}
}
该类的作用是执行代理监听类的实现类,例如<input type="button" onclick="func()" />
onclick是代理监听类,func相当于ProxyFactory,而实现类就是StatementHandler;
public class ProxyFactory implements InvocationHandler {
private Configuration configuration;
private Class classFile;
public ProxyFactory(Configuration configuration,Class classFile){
this.configuration = configuration;
this.classFile = classFile;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String classFullName = classFile.getName();
int size = classFullName.lastIndexOf(".");
String methodName = classFullName.substring(size + 1) +"."+ method.getName();
StatementHandler statementHandler = new StatementHandler(configuration,methodName);
return statementHandler.execute();
}
}
该类相当于代理监听类的实现类,它主要是获取代理类中需要执行的方法,根据方法名在configuration中获取对应方法需要执行的sql语句
public class StatementHandler {
private Configuration configuration;
private Statement statement;
private String method;
public StatementHandler(Configuration configuration,String method){
this.method = method;
this.configuration = configuration;
}
public Object execute() throws Exception{
Map sqlMap = configuration.getSqlMap();
Connection connection = configuration.getConnection();
statement = connection.createStatement();
statement.execute(sqlMap.get(method).toString());
ResultSet resultSet = statement.getResultSet();
List list = new ArrayList();
while (resultSet.next()){
Class classFile = Class.forName((String) configuration.getTypeMap().get(method));
Field fields[] = classFile.getDeclaredFields();
Object classObj = classFile.newInstance();
for (int i=0;i<fields.length;i++){
Field field = fields[i];
String name = field.getName();
Class typeClass = field.getType();
field.setAccessible(true);
String resultValue = resultSet.getString(name);
if (Integer.class == typeClass){
field.set(classObj,Integer.valueOf(resultValue));
} else if (String.class == typeClass){
field.set(classObj,String.valueOf(resultValue));
}
}
list.add(classObj);
}
close();
return list;
}
public void close(){
try {
if (statement != null){
statement.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
该类的最要作用是获取代理监听类,getMapper返回的是代理类
public class SqlSessionFactory {
private Configuration configuration;
public SqlSessionFactory(Configuration configuration){
this.configuration = configuration;
}
public Object getMapper(Class classFile){
ProxyFactory proxyFactory = new ProxyFactory(configuration,classFile);
Class classArray[] = {classFile};
return Proxy.newProxyInstance(classFile.getClassLoader(),classArray,proxyFactory);
}
}
测试类
public class TestMain {
private SqlSessionFactory sqlSessionFactory;
private Configuration configuration;
@Before
public void before(){
configuration = new Configuration("mapper");
sqlSessionFactory = new SqlSessionFactory(configuration);
}
@After
public void after(){
configuration.close();
}
@Test
public void test(){
DeptDao deptDao = (DeptDao) sqlSessionFactory.getMapper(DeptDao.class);
List list = deptDao.findAll();
System.out.println(list.toString());
}
}