如題,此次我又來做死試試編寫相似Mybatis的持久層框架了java
建立一個接口UserMapper,再建立一個實體類User
使用JDK的動態代理,建立一個代理處理器sql
public class InvocationHandlerMybatis implements InvocationHandler {
/** * * @param proxy 代理對象 * @param method 攔截的方法 * @param args 方法上的參數 * @return * @throws Throwable */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始代理");
return 1;
}
}
複製代碼
包裝上面的代理數組
public class SqlSession {
/**加載Mapper接口*/
public static <T> T getMapper(Class clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandlerMybatis());
}
}
複製代碼
使用測試類測試一下bash
public static void main(String[] args) {
UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
int i = userMapper.insertUser("", "");
System.out.println(i);
}
複製代碼
測試結果以下 app
這樣就能夠實現拿到接口的方法參數而且自行控制對象的返回值@Insert
的實現步驟@Insert
註解,存在的話,獲取上面的SQL語句/** * @author libi * 用於動態代理,獲取方法的參數而且給返回值 */
public class InvocationHandlerMybatis implements InvocationHandler {
/** * @param proxy 代理對象 * @param method 攔截的方法 * @param args 方法上的參數 * @return 方法的返回值 * @throws Throwable */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始代理");
//判斷方法上是否存在Insert註解
ExtInsert extInsert = method.getDeclaredAnnotation(ExtInsert.class);
if (extInsert != null) {
//執行插入的操做,返回影響行數
return doInsert(method, args, extInsert);
}
return null;
}
/** * 執行插入的操做 * @param method * @param args * @param extInsert * @return */
private int doInsert(Method method, Object[] args, ExtInsert extInsert) {
//獲取Sql語句
String sql = extInsert.value();
System.out.println("insert sql:" + sql);
//獲取方法參數和Sql語句進行匹配
//定義一個Map,Key是參數名,Value是參數值
ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
//獲取方法上的參數
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
//獲取參數名稱和參數的值
ExtParam param = parameters[i].getDeclaredAnnotation(ExtParam.class);
if (param != null) {
String name = param.value();
Object value = args[i];
System.out.println("paramName:"+name+",paramValue:"+value);
map.put(name, value);
}
}
//怕打亂順序而把sql語句的參數放在一個有序的數組裏
List<Object> sqlParam = new ArrayList<>();
String[] sqlInsertParameter = SQLUtils.sqlInsertParameter(sql);
for (String paramName : sqlInsertParameter) {
Object paramValue = map.get(paramName);
sqlParam.add(paramValue);
}
System.out.println();
//把參數替換成?
sql = SQLUtils.parameQuestion(sql, sqlInsertParameter);
System.out.println("new sql:"+sql);
//執行JDBC
return JDBCUtils.insert(sql, false, sqlParam);
}
}
複製代碼
咱們定義一個Mapper框架
public interface UserMapper {
@ExtInsert("insert into user(username,password) values (#{userName},#{password})")
int insertUser(@ExtParam("userName") String userName, @ExtParam("password") String password);
}
複製代碼
而後再主函數裏使用代理調用這個方法ide
public class Cluster {
public static void main(String[] args) {
UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
int i = userMapper.insertUser("name", "123");
System.out.println(i);
}
}
複製代碼
而後運行結果以下 函數
@Select
的實現思路@Select
註解的方法,拿到Spl語句和上面不一樣的是,我重構了InvocationHandleMybatis類的代碼,複用了一些代碼測試
/**
* 執行查詢的操做
* @param method
* @param args
* @param extSelect
* @return 查詢結果,多是實體類對象,List或者基礎類型
*/
private Object doSelect(Method method, Object[] args, ExtSelect extSelect) throws SQLException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//獲取Sql語句
String sql = extSelect.value();
System.out.println("select sql:" + sql);
//獲取方法參數和Sql語句進行匹配
ConcurrentHashMap<String, Object> paramMap = getParamMap(method, args);
//怕Sql參數順序和@Param參數順序不一致而把sql語句的參數放在一個有序的數組裏
List<Object> sqlParamValue = new ArrayList<>();
List<String> sqlSelectParameter = SQLUtils.sqlSelectParameter(sql);
for (String paramName : sqlSelectParameter) {
Object paramValue = paramMap.get(paramName);
sqlParamValue.add(paramValue);
}
//把參數替換成?
sql = SQLUtils.parameQuestion(sql, sqlSelectParameter);
System.out.println("new sql:"+sql);
//執行JDBC
ResultSet resultSet = JDBCUtils.query(sql, sqlParamValue);
//判斷是否有結果集
if (!resultSet.next()) {
return null;
}
resultSet.previous();
//使用反射獲取方法類型
Class<?> returnType = method.getReturnType();
//使用反射機制實例化對象
Object result = returnType.newInstance();
//遍歷這個結果集
while (resultSet.next()) {
for (String paramName : sqlSelectParameter) {
//獲取參數的值
Object resultValue = resultSet.getObject(paramName);
//使用反射機制賦值
Field field = returnType.getDeclaredField(paramName);
field.setAccessible(true);
field.set(result, resultValue);
}
}
return result;
}
複製代碼
在使用上面的代碼時,我會檢測方法上是否有@Select
註解,有的話說明這個方法是用於查詢語句的,咱們就把這個註解傳進來
咱們改寫UserMapper類,增長Select方法,以下ui
public interface UserMapper {
@ExtInsert("insert into user(username,password) values (#{userName},#{password})")
int insertUser(@ExtParam("userName") String userName, @ExtParam("password") String password);
@ExtSelect("select * from user where username=#{userName} and password=#{password}")
User selectUser(@ExtParam("userName") String userName, @ExtParam("password") String password);
}
複製代碼
改寫測試用的代碼,以下
public class Cluster {
public static void main(String[] args) {
UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUser("name", "123");
System.out.println(user.getUserName());
}
}
複製代碼
執行後個人運行結果以下
/** * @author libi * 用於動態代理,獲取方法的參數而且給返回值 */
public class InvocationHandlerMybatis implements InvocationHandler {
/** * @param proxy 代理對象 * @param method 攔截的方法 * @param args 方法上的參數 * @return 方法的返回值 * @throws Throwable */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始代理");
//判斷方法上是否存在Insert註解
ExtInsert extInsert = method.getDeclaredAnnotation(ExtInsert.class);
if (extInsert != null) {
//執行插入的操做,返回影響行數
return doInsert(method, args, extInsert);
}
//判斷方法上是否有Select註解
ExtSelect extSelect = method.getDeclaredAnnotation(ExtSelect.class);
if (extSelect != null) {
//執行查詢的操做,返回實際實體類或者List
return doSelect(method, args, extSelect);
}
return null;
}
/** * 執行插入的操做 * @param method * @param args * @param extInsert * @return 影響行數 */
private int doInsert(Method method, Object[] args, ExtInsert extInsert) {
//獲取Sql語句
String sql = extInsert.value();
System.out.println("insert sql:" + sql);
//獲取方法參數和Sql語句進行匹配
ConcurrentHashMap<String, Object> paramMap = getParamMap(method, args);
//怕Sql參數順序和@Param參數順序不一致而把sql語句的參數放在一個有序的數組裏
List<Object> sqlParamValue = new ArrayList<>();
String[] sqlInsertParameter = SQLUtils.sqlInsertParameter(sql);
for (String paramName : sqlInsertParameter) {
Object paramValue = paramMap.get(paramName);
sqlParamValue.add(paramValue);
}
//把參數替換成?
sql = SQLUtils.parameQuestion(sql, sqlInsertParameter);
System.out.println("new sql:"+sql);
//執行JDBC
return JDBCUtils.insert(sql, false, sqlParamValue);
}
/** * 執行查詢的操做 * @param method * @param args * @param extSelect * @return 查詢結果,多是實體類對象,List或者基礎類型 */
private Object doSelect(Method method, Object[] args, ExtSelect extSelect) throws SQLException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//獲取Sql語句
String sql = extSelect.value();
System.out.println("select sql:" + sql);
//獲取方法參數和Sql語句進行匹配
ConcurrentHashMap<String, Object> paramMap = getParamMap(method, args);
//怕Sql參數順序和@Param參數順序不一致而把sql語句的參數放在一個有序的數組裏
List<Object> sqlParamValue = new ArrayList<>();
List<String> sqlSelectParameter = SQLUtils.sqlSelectParameter(sql);
for (String paramName : sqlSelectParameter) {
Object paramValue = paramMap.get(paramName);
sqlParamValue.add(paramValue);
}
//把參數替換成?
sql = SQLUtils.parameQuestion(sql, sqlSelectParameter);
System.out.println("new sql:"+sql);
//執行JDBC
ResultSet resultSet = JDBCUtils.query(sql, sqlParamValue);
//判斷是否有結果集
if (!resultSet.next()) {
return null;
}
resultSet.previous();
//使用反射獲取方法類型
Class<?> returnType = method.getReturnType();
//使用反射機制實例化對象
Object result = returnType.newInstance();
//遍歷這個結果集
while (resultSet.next()) {
for (String paramName : sqlSelectParameter) {
//獲取參數的值
Object resultValue = resultSet.getObject(paramName);
//使用反射機制賦值
Field field = returnType.getDeclaredField(paramName);
field.setAccessible(true);
field.set(result, resultValue);
}
}
return result;
}
/** * 創建方法上的參數和值@Param參數名的映射 * @param method * @param args * @return */
private ConcurrentHashMap<String, Object> getParamMap(Method method, Object[] args) {
//定義一個Map,Key是參數名,Value是參數值
ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
//獲取方法上的參數
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
//獲取參數名稱和參數的值
ExtParam param = parameters[i].getDeclaredAnnotation(ExtParam.class);
if (param != null) {
String name = param.value();
Object value = args[i];
System.out.println("paramName:"+name+",paramValue:"+value);
map.put(name, value);
}
}
return map;
}
}
複製代碼