上一篇:(五)在WEB中应用MyBatis(使用MVC架构模式)
下一篇:(七)Mybatis传值中#{}和${}的区别,及别名机制
在上一篇的学习中,不难发现dao层中的实现类代码比较固定,基本就是创建SqlSession对象,然后通过SqlSession对象调用CURD的相关方法,这个类中的方法没有任何业务逻辑,那可以可以动态生成呢?
可以,我们可以使用javassist动态生成代理类。
public class AccountDaoImpl implements AccountDao{
@Override
public Account selectByActno(String actno) {
SqlSession session = SqlSessionUtil.getSession();
Account account = session.selectOne("selectByActno", actno);
return account;
}
@Override
public int updateByActno(Account act) {
SqlSession session = SqlSessionUtil.getSession();
int count = session.update("updateByActno",act);
return count;
}
}
在Mybatis中已经内置了javassist,无需再次引入依赖
在上一篇:(五)在WEB中应用MyBatis(使用MVC架构模式)的基础上写一个工具类,动态生成dao接口的代理实现类,通过这个代理类调用接口中的方法。
动态生成dao接口的实现类步骤
public class GenerateDaoProxy {
private GenerateDaoProxy() {//工具类构造方法私有化
}
/**
* 生成dao接口的实现类,将接口的实现类创建并返回
* @param sqlSession SqlSession对象
* @param daoInterface dao目标接口
* @return 实现类的实例化对象
*/
public static Object generate(SqlSession sqlSession,Class daoInterface){
//1.获取类池
ClassPool pool = ClassPool.getDefault();
//2.制造类
CtClass ctClass = pool.makeClass(daoInterface.getName()+"Proxy");
//3.制造接口
CtClass ctInterface = pool.makeInterface(daoInterface.getName());
//4.实现接口
ctClass.addInterface(ctInterface);
//5.制造并实现方法
//5.1 获取所有的方法
Method[] methods = daoInterface.getDeclaredMethods();
//5.2通过遍历循环动态制造方法,并将方法添加到类中
Arrays.stream(methods).forEach(method -> {
try {
//通过StringBuffer动态制造方法
StringBuffer methodCode = new StringBuffer();
methodCode.append("public ");//接口一定是public
methodCode.append(method.getReturnType().getName() + " ");//追加返回值类型的简称,因为有可能引用类型是带包名的
methodCode.append(method.getName());//追加方法名
methodCode.append("(");
//通过循环遍历动态获取参数类型,追加参数
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
methodCode.append(parameterType.getName() + " ");//追加参数类型
methodCode.append("arg" + i);//追加参数名
if (i != parameterTypes.length-1) {//如果不是最后一个,这追加逗号
methodCode.append(",");
}
}
methodCode.append(")");
methodCode.append("{");
//这里需要使用全限定类名,要不然系统找不到
methodCode.append("org.apache.ibatis.session.SqlSession session = com.bank.utils.SqlSessionUtil.getSession();");
//这里需要知道什么类型的sql语句
//这里还需要知道sql语句的id,而sql的id具有多变性,所以规定:
//凡是使用GenerateDaoProxy的,namespace就必须是dao接口的全限定名称,id必须是dao接口的方法名
String sqlid = daoInterface.getName() + "." + method.getName();//这个id就是接口全限定名称加方法名
//SqlCommandType是个枚举类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
//分别表示未知,插入,更新,删除,选择,刷新
SqlCommandType sqlCommandType = sqlSession.getConfiguration().getMappedStatement(sqlid).getSqlCommandType();
if (sqlCommandType == SqlCommandType.INSERT) {
//insert操作
}
if (sqlCommandType == SqlCommandType.DELETE) {
//delete操作
}
if (sqlCommandType == SqlCommandType.UPDATE) {
methodCode.append("return session.update(\""+sqlid+"\",arg0);");//arg0是拼接的方法参数
}
if (sqlCommandType == SqlCommandType.SELECT) {
methodCode.append("return ("+method.getReturnType().getName()+")session.selectOne(\""+sqlid+"\",arg0);");
}
methodCode.append("}");
System.out.println(methodCode.toString());
CtMethod ctMethod = CtMethod.make(methodCode.toString(), ctClass);
ctClass.addMethod(ctMethod);
} catch (Exception e) {
e.printStackTrace();
}
});
//6.创建对象并返回
Object obj = null;
try {
Class<?> clazz = ctClass.toClass();
obj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
在service里就可以把new 实现类换成我们封装的动态生成类
//private AccountDao accountDao = new AccountDaoImpl();//这里就可以换成下面的
//自己封装的
private AccountDao accountDao = (AccountDao) GenerateDaoProxy.generate(SqlSessionUtil.getSession(),AccountDao.class);

其实以上内容mybatis内部已经实现了。直接调用以下代码即可获取dao接口的代理类:
private AccountDao accountDao = SqlSessionUtil.getSession().getMapper(AccountDao.class);
与自己封装的一样,由于Mybatis需要知道你的sql的id,而sql的id具有多变性,所以mybatis开发者出台了一个规定,
在SqlMapper.xml映射文件里面,namespace必须和dao接口的全限定名称一致,id必须和dao接口中方法名一致。