IBatis Mapper&Spring Data JPA实现原理

      因为我们最近的一个项目数据库访问呢层使用Ibatis, 今天团队成员问Ibatis中只写接口,不写实现,Ibatis是如何帮助我们查询数据。其实原理很简单,就是Java的反射和代理,因为Java的代理是真对于接口的。所以我们就可以在开发中DAO模块就直接写接口和对用的SQL就可以。实现类由我们生成代理,当代理方法被调用的时候我们就使用通用的数据库访问方法去接管它,为清晰简单的说明原理,这里直接给代码。

1public class User { 2 private long id; 3 private String name; 4 5 public long getId() { 6 return id; 7 } 8 9 public void setId(long id) { 10 this.id = id; 11 } 12 13 public String getName() { 14 return name; 15 } 16 17 public void setName(String name) { 18 this.name = name; 19 } 20 21} 22 23@Documented 24@Retention(RetentionPolicy.RUNTIME) 25@Target(ElementType.METHOD) 26public @interface Query { 27 String value(); 28} 29 30@Documented 31@Retention(RetentionPolicy.RUNTIME) 32@Target(ElementType.METHOD) 33public @interface Update { 34 String value(); 35} 36 37@Documented 38@Retention(RetentionPolicy.RUNTIME) 39@Target(ElementType.METHOD) 40public @interface Insert { 41 String value(); 42} 43 44@Documented 45@Retention(RetentionPolicy.RUNTIME) 46@Target(ElementType.PARAMETER) 47public @interface Param { 48 String value(); 49} 50 51public interface UserDao { 52 53 @Update("delete from t_user where id=:id") 54 void delete(@Param("id")long id); 55 56 @Insert("insert t_user(id,name) values(:id,:name)") 57 long add(@Param("id")long id,@Param("name")String name); 58 59 @Update("update t_user set name=:name where id=:id") 60 boolean modify(@Param("name")String name,@Param("id") long id); 61 62 @Query("Select * from t_user id=:id") 63 User find(@Param("id")long id); 64 65}

关键代码1

1import java.lang.annotation.Annotation; 2import java.lang.reflect.InvocationHandler; 3import java.lang.reflect.Method; 4import java.sql.Connection; 5import java.sql.DriverManager; 6import java.sql.SQLException; 7 8import java.util.logging.Level; 9import java.util.logging.Logger; 10 11/** 12 * 13 * @author Silence 14 */ 15public class DaoInvocationHandlerImpl implements InvocationHandler { 16 17 private static Connection conn = null; 18 19 static{ 20 try { 21 conn = DriverManager.getConnection("jdbc:mysql://localhost/test?useUnicode=true&amp;characterEncoding=UTF-8&amp;autoReconnect=true", "test", "test"); 22 } catch (SQLException ex) { 23 Logger.getLogger(DaoInvocationHandlerImpl.class.getName()).log(Level.SEVERE, null, ex); 24 } 25 } 26 27 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 28 Logger.getLogger(DaoInvocationHandlerImpl.class.getName()).log(Level.SEVERE, "{0} invoke", method.getName()); 29 30 Annotation[][] annos = method.getParameterAnnotations(); 31 try { 32 if (method.getAnnotation(Query.class) != null) { 33 Query query = method.getAnnotation(Query.class); 34 NamedParameterStatement nps = new NamedParameterStatement(conn, query.value()); 35 for(int i=0;i<annos.length;i++){ 36 for(Annotation an:annos[i]){ 37 if(an instanceof Param){ 38 nps.setObject(((Param)an).value(), args[i]); 39 } 40 } 41 } 42 //这里也可以再次包装 43 return nps.executeQuery(); 44 } else if (method.getAnnotation(Insert.class) != null) { 45 //代码省略 46 return null; 47 } else if (method.getAnnotation(Update.class) != null){ 48 //代码省略 49 return null; 50 } else { 51 //代码省略 52 return null; 53 } 54 } catch (Exception e) { 55 Logger.getLogger(DaoInvocationHandlerImpl.class.getName()).log(Level.SEVERE, "error occurs in [" + method.toGenericString() + "]", e); 56 throw e; 57 } 58 } 59 60} 61 62//这段代码来自网上的主要用于命名参数,就是把sql中的参数替换成问号,并设置对应的值 63import java.sql.Connection; 64import java.sql.PreparedStatement; 65import java.sql.ResultSet; 66import java.sql.SQLException; 67import java.sql.Timestamp; 68import java.util.HashMap; 69import java.util.Iterator; 70import java.util.LinkedList; 71import java.util.List; 72import java.util.Map; 73 74/** 75 * 76 * @author Silence 77 */ 78public class NamedParameterStatement { 79 /** The statement this object is wrapping. */ 80 private final PreparedStatement statement; 81 82 /** Maps parameter names to arrays of ints which are the parameter indices. 83*/ 84 private final Map indexMap; 85 86 87 /** 88 * Creates a NamedParameterStatement. Wraps a call to 89 * c.{@link Connection#prepareStatement(java.lang.String) 90prepareStatement}. 91 * @param connection the database connection 92 * @param query the parameterized query 93 * @throws SQLException if the statement could not be created 94 */ 95 public NamedParameterStatement(Connection connection, String query) throws SQLException { 96 indexMap=new HashMap(); 97 String parsedQuery=parse(query, indexMap); 98 statement=connection.prepareStatement(parsedQuery); 99 } 100 101 102 /** 103 * Parses a query with named parameters. The parameter-index mappings are 104put into the map, and the 105 * parsed query is returned. DO NOT CALL FROM CLIENT CODE. This 106method is non-private so JUnit code can 107 * test it. 108 * @param query query to parse 109 * @param paramMap map to hold parameter-index mappings 110 * @return the parsed query 111 */ 112 static final String parse(String query, Map paramMap) { 113 // I was originally using regular expressions, but they didn't work well for ignoring 114 // parameter-like strings inside quotes. 115 int length=query.length(); 116 StringBuffer parsedQuery=new StringBuffer(length); 117 boolean inSingleQuote=false; 118 boolean inDoubleQuote=false; 119 int index=1; 120 121 for(int i=0;i<length;i++) { 122 char c=query.charAt(i); 123 if(inSingleQuote) { 124 if(c=='\'') { 125 inSingleQuote=false; 126 } 127 } else if(inDoubleQuote) { 128 if(c=='"') { 129 inDoubleQuote=false; 130 } 131 } else { 132 if(c=='\'') { 133 inSingleQuote=true; 134 } else if(c=='"') { 135 inDoubleQuote=true; 136 } else if(c==':' && i+1<length && 137 Character.isJavaIdentifierStart(query.charAt(i+1))) { 138 int j=i+2; 139 while(j<length && Character.isJavaIdentifierPart(query.charAt(j))) { 140 j++; 141 } 142 String name=query.substring(i+1,j); 143 c='?'; // replace the parameter with a question mark 144 i+=name.length(); // skip past the end if the parameter 145 146 List indexList=(List)paramMap.get(name); 147 if(indexList==null) { 148 indexList=new LinkedList(); 149 paramMap.put(name, indexList); 150 } 151 indexList.add(new Integer(index)); 152 153 index++; 154 } 155 } 156 parsedQuery.append(c); 157 } 158 159 // replace the lists of Integer objects with arrays of ints 160 for(Iterator itr=paramMap.entrySet().iterator(); itr.hasNext();) { 161 Map.Entry entry=(Map.Entry)itr.next(); 162 List list=(List)entry.getValue(); 163 int[] indexes=new int[list.size()]; 164 int i=0; 165 for(Iterator itr2=list.iterator(); itr2.hasNext();) { 166 Integer x=(Integer)itr2.next(); 167 indexes[i++]=x.intValue(); 168 } 169 entry.setValue(indexes); 170 } 171 172 return parsedQuery.toString(); 173 } 174 175 176 /** 177 * Returns the indexes for a parameter. 178 * @param name parameter name 179 * @return parameter indexes 180 * @throws IllegalArgumentException if the parameter does not exist 181 */ 182 private int[] getIndexes(String name) { 183 int[] indexes=(int[])indexMap.get(name); 184 if(indexes==null) { 185 throw new IllegalArgumentException("Parameter not found: "+name); 186 } 187 return indexes; 188 } 189 190 191 /** 192 * Sets a parameter. 193 * @param name parameter name 194 * @param value parameter value 195 * @throws SQLException if an error occurred 196 * @throws IllegalArgumentException if the parameter does not exist 197 * @see PreparedStatement#setObject(int, java.lang.Object) 198 */ 199 public void setObject(String name, Object value) throws SQLException { 200 int[] indexes=getIndexes(name); 201 for(int i=0; i < indexes.length; i++) { 202 statement.setObject(indexes[i], value); 203 } 204 } 205 206 207 /** 208 * Sets a parameter. 209 * @param name parameter name 210 * @param value parameter value 211 * @throws SQLException if an error occurred 212 * @throws IllegalArgumentException if the parameter does not exist 213 * @see PreparedStatement#setString(int, java.lang.String) 214 */ 215 public void setString(String name, String value) throws SQLException { 216 int[] indexes=getIndexes(name); 217 for(int i=0; i < indexes.length; i++) { 218 statement.setString(indexes[i], value); 219 } 220 } 221 222 223 /** 224 * Sets a parameter. 225 * @param name parameter name 226 * @param value parameter value 227 * @throws SQLException if an error occurred 228 * @throws IllegalArgumentException if the parameter does not exist 229 * @see PreparedStatement#setInt(int, int) 230 */ 231 public void setInt(String name, int value) throws SQLException { 232 int[] indexes=getIndexes(name); 233 for(int i=0; i < indexes.length; i++) { 234 statement.setInt(indexes[i], value); 235 } 236 } 237 238 239 /** 240 * Sets a parameter. 241 * @param name parameter name 242 * @param value parameter value 243 * @throws SQLException if an error occurred 244 * @throws IllegalArgumentException if the parameter does not exist 245 * @see PreparedStatement#setInt(int, int) 246 */ 247 public void setLong(String name, long value) throws SQLException { 248 int[] indexes=getIndexes(name); 249 for(int i=0; i < indexes.length; i++) { 250 statement.setLong(indexes[i], value); 251 } 252 } 253 254 255 /** 256 * Sets a parameter. 257 * @param name parameter name 258 * @param value parameter value 259 * @throws SQLException if an error occurred 260 * @throws IllegalArgumentException if the parameter does not exist 261 * @see PreparedStatement#setTimestamp(int, java.sql.Timestamp) 262 */ 263 public void setTimestamp(String name, Timestamp value) throws SQLException 264{ 265 int[] indexes=getIndexes(name); 266 for(int i=0; i < indexes.length; i++) { 267 statement.setTimestamp(indexes[i], value); 268 } 269 } 270 271 272 /** 273 * Returns the underlying statement. 274 * @return the statement 275 */ 276 public PreparedStatement getStatement() { 277 return statement; 278 } 279 280 281 /** 282 * Executes the statement. 283 * @return true if the first result is a {@link ResultSet} 284 * @throws SQLException if an error occurred 285 * @see PreparedStatement#execute() 286 */ 287 public boolean execute() throws SQLException { 288 return statement.execute(); 289 } 290 291 292 /** 293 * Executes the statement, which must be a query. 294 * @return the query results 295 * @throws SQLException if an error occurred 296 * @see PreparedStatement#executeQuery() 297 */ 298 public ResultSet executeQuery() throws SQLException { 299 return statement.executeQuery(); 300 } 301 302 303 /** 304 * Executes the statement, which must be an SQL INSERT, UPDATE or DELETE 305statement; 306 * or an SQL statement that returns nothing, such as a DDL statement. 307 * @return number of rows affected 308 * @throws SQLException if an error occurred 309 * @see PreparedStatement#executeUpdate() 310 */ 311 public int executeUpdate() throws SQLException { 312 return statement.executeUpdate(); 313 } 314 315 316 /** 317 * Closes the statement. 318 * @throws SQLException if an error occurred 319 * @see Statement#close() 320 */ 321 public void close() throws SQLException { 322 statement.close(); 323 } 324 325 326 /** 327 * Adds the current set of parameters as a batch entry. 328 * @throws SQLException if something went wrong 329 */ 330 public void addBatch() throws SQLException { 331 statement.addBatch(); 332 } 333 334 335 /** 336 * Executes all of the batched statements. 337 * 338 * See {@link Statement#executeBatch()} for details. 339 * @return update counts for each statement 340 * @throws SQLException if something went wrong 341 */ 342 public int[] executeBatch() throws SQLException { 343 return statement.executeBatch(); 344 } 345}

关键代码2

1public class Test { 2 3 public static void main(String[] args) { 4 //如果使用spring,可以实现spring工厂bean接口包装这段代码,为server类提供注入 5 UserDao dao = (UserDao) Proxy.newProxyInstance( 6 DaoInvocationHandlerImpl.class.getClassLoader(), 7 new Class[]{UserDao.class}, 8 new DaoInvocationHandlerImpl()); 9 dao.find(1L); 10 } 11 12}

以上代码比较粗糙,只用于技术实现验证使用;我没有测试上面代码,感兴趣的农码可以试试,O(∩_∩)O哈哈~

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )