Framework Optimization
The previous section implemented part of the content. Let’s continue below.
Above we wrote a custom framework that solved a series of problems brought by JDBC, but some problems have also appeared:
- DAO implementation class has repeated code, entire operation template repeats, creating SqlSession, etc.
- DAO implementation class has hard-coded values, Statement ID parameters are hard-coded when calling SqlSession methods
SqlSession
Solution: Use proxy pattern to create interface proxy objects. We add a new method to SqlSession: getMapper
<T> T getMapper(Class<?> mapperClass);
DefaultSqlSession
We implement the getMapper method in the implementation class:
@Override
public <T> T getMapper(Class<?> mapperClass) {
Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
String className = method.getDeclaringClass().getName();
String statementId = className + "." + methodName;
Type genericReturnType = method.getGenericReturnType();
if(genericReturnType instanceof ParameterizedType){
List<Object> objects = selectList(statementId, args);
return objects;
}
return selectOne(statementId,args);
}
});
return (T) proxyInstance;
}
Code Analysis
Method Description:
@Override: Indicates the method overrides parent class or interface methodT: Defines a generic method, T is the return value typegetMapper(Class<?> mapperClass): Method accepts a Class type parameter representing the type of Mapper interface to generate
Proxy.newProxyInstance Parameters:
DefaultSqlSession.class.getClassLoader(): Specifies class loadernew Class[]{mapperClass}: Specifies interface array that proxy class needs to implementnew InvocationHandler(): Passes InvocationHandler to handle method call logic
invoke Method Parameters:
Object proxy: The proxy object itselfMethod method: The currently called method objectObject[] args: Parameters passed when calling method
Method Call Logic
String methodName = method.getName();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
Get method name, check if method is a method in Object class (such as toString(), equals(), etc.). If so, directly call original implementation.
SQL Statement Identifier
String className = method.getDeclaringClass().getName();
String statementId = className + "." + methodName;
Concatenate method’s class name and method name to generate unique SQL statement identifier.
Method Return Type Judgment
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
List<Object> objects = selectList(statementId, args);
return objects;
}
If return type is parameterized type (such as List), call selectList method to execute query; otherwise call selectOne.
Test Method
package icu.wzk.test;
import icu.wzk.bean.Resources;
import icu.wzk.bean.SqlSession;
import icu.wzk.bean.SqlSessionFactory;
import icu.wzk.bean.SqlSessionFactoryBuilder;
import icu.wzk.dao.UserInfoMapper;
import icu.wzk.model.UserInfo;
import java.io.InputStream;
public class Test02 {
public static void main(String[] args) throws Exception {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserInfo userInfo = new UserInfo();
userInfo.setUsername("wzk");
UserInfoMapper userInfoMapper = sqlSession.getMapper(UserInfoMapper.class);
System.out.println("userInfoMapper: " + userInfoMapper);
System.out.println(userInfoMapper.selectOne(userInfo));
}
}
Running Results
log4j:WARN No appenders could be found for logger (com.mchange.v2.log.MLog).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
userInfoMapper: icu.wzk.bean.DefaultSqlSession$1@61dc03ce
SimpleExecutor getBoundSql: SELECT * FROM user_info WHERE username=?
UserInfo(id=1, username=wzk, password=icu, age=18)