聊聊、手写Mybatis XML配置方式

导航:  

聊聊、Mybatis API  

聊聊、Mybatis XML 

聊聊、Mybatis集成Spring XML 方式

聊聊、Mybatis集成Spring 注解方式 

聊聊、手写Mybatis 注解配置方式 

聊聊、手写Mybatis SpringBoot Starter

 

首先,提出一个问题,怎么通过 mapperInterface 就能拿到数据的呢? 

AccountMapper.java


 public interface AccountMapper {
  @Select("select * from account")
  public List<Map<String,Object>> queryAll();
}

 

在 main 方法中只要 AccountMapper mapper = (AccountMapper) getMapper(AccountMapper.class);mapper.queryAll();

这里的 mapper 如果是个接口类,怎么去调用方法?怎么实例化?所以这里的 mapper 一定是个代理类。

我们先来了解动态代理,实现动态代理的方式有很多种,例如:cglib、jdk动态代理,javassist。具体可以参考《聊聊、动态代理》。

我们来看看,Mybatis 中怎么用动态代理的,我们用 JDK动态代理 实现 mapper 代理类。

 

AccountInvokers.java 


public class AccountInvokers implements InvocationHandler {

public AccountInvokers() {
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  //通过 Method 拿到 @Select 注解的 value,也就是 @Select("select * from account") 中的 SQL 语句 

  Select select = method.getAnnotation(Select.class);

  //这里的 sql = select * from account

  String sql = select.value()[0]; 

  // 通过 sql 语句,查询数据库,没错,下面就是我们很熟悉的 JDBC 操作
  Class.forName("com.mysql.jdbc.Driver");
  Connection con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?useSSL=false", "root", "root");
  Statement st = con.createStatement();
  ResultSet rs = st.executeQuery(sql);

 //封装成返回的对象类型

  List<Map<String,Object>> list = new ArrayList<>();
  while (rs.next()){
      Map<String,Object> map = new HashMap<>();
      int id = rs.getInt("id");
     String name = rs.getString("NAME");
     map.put("id",id);
     map.put("name",name);
     list.add(map);
  }
  st.close();
  con.close();
  return list;
}
} 

好了,代理逻辑就是上面的代码,我们来调用看看。 

 

Main方法 


public class ManualMain {

public static void main(String[] args) {
  AccountMapper mapper = (AccountMapper) getMapper(AccountMapper.class);
  System.out.println(mapper.queryAll());
}

public static Object getMapper(Class clazz){
  Object instance = Proxy.newProxyInstance(ManualMain.class.getClassLoader(), new Class[]{clazz}, new AccountInvokers());
  return instance;
}
}

 

在 AccountInvokers 中,其实是有数据库连接操作逻辑的,所以我们调用 mapper.queryAll() 就能拿到数据。但这里代码还不够完善,因为并没有集成 Spring。

这里只是解决了动态代理,还没有交给 Spring 管理。如果要交给 Spring 管理,我们实现自己的 AccountMapperFactoryBean,类似 MapperFactoryBean。

 

AccountMapperFactoryBean 


package org.rockcode.factory;

import org.rockcode.invokers.AccountInvokers;
import org.rockcode.mappers.AccountMapper;
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.Proxy;
import org.springframework.stereotype.Component; 

@Component
public class AccountMapperFactoryBean implements FactoryBean {

@Override
public Object getObject() {
  return getMapper(AccountMapper.class);
}

private Object getMapper(Class clazz){
  Object instance = Proxy.newProxyInstance(AccountFactoryBean.class.getClassLoader(), new Class[]{clazz}, new AccountInvokers());
  return instance;
}

@Override
public Class<?> getObjectType() {
  return AccountMapper.class;
}
}

 

AccountConfig 


package org.rockcode.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan({"org.rockcode.factory"})
public class AccountConfig {
}

  

Main方法 


public class ManualMain {

public static void main(String[] args) {  

  AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AccountConfig.class);
  AccountMapperFactoryBean accountFactoryBean = ac.getBean(AccountFactoryBean.class);
  AccountMapper accountMapper = (AccountMapper) accountFactoryBean.getObject();
  System.out.println(accountMapper.queryAll());

}
} 

 

上面的 AccountMapperFactoryBean 实现了 FactoryBean,这个扩展点在于 Spring 从 getObject() 方法中获得 Bean 实体。但相比 MapperFactoryBean,还少了 mapperInterface  和 sqlSessionFactory 两个属性。

继续优化 AccountMapperFactoryBean 

 

AccountMapperFactoryBean 


package org.rockcode.factory;


import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.FactoryBean;
import static org.springframework.util.Assert.notNull;

public class AccountMapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean {

private Class mapperInterface;

public void setMapperInterface(Class mapperInterface) {
  this.mapperInterface = mapperInterface;
}

@Override
protected void checkDaoConfig() {
  super.checkDaoConfig();
  notNull(this.mapperInterface, "Property 'mapperInterface' is required");
  Configuration configuration = getSqlSession().getConfiguration();
  configuration.addMapper(this.mapperInterface);
}

@Override
public Object getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

@Override
public Class<?> getObjectType() {
  return this.mapperInterface;
}
}

 

spring.xml 


<bean id="accountMapper" class="org.rockcode.factory.AccountMapperFactoryBean">
  <property name="mapperInterface" value="org.rockcode.mappers.AccountMapper"/>
  <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

  

AccountController


@Controller

@RequestMapping
public class AccountController { 

@Autowired
private AccountMapper accountMapper; 

@RequestMapping("/getAllAccount")
@ResponseBody
public String getAllAccount(){
  accountMapper.queryAll();
  return "getAllAccount";
} 

}

上面的实现用到了 FactoryBean,也用到了 SqlSessionDaoSupport,而 SqlSessionDaoSupport 中恰好用到了 InitializingBean 扩展点。

就是在 InitializingBean  的 afterPropertiesSet() 方法中,调用了 checkDaoConfig() 方法。

好了,到这里,手写 Mybatis 已经完成了一大半,实现了自己的 AccountMapperFactoryBean ,但是这个 AccountMapperFactoryBean 需要 XML 配置。如果是注解方式来实现手写 Mybatis,那有该怎么做呢?

 

posted @ 2020-04-03 11:07  香农随笔  阅读(582)  评论(0编辑  收藏  举报