IBatis Mapper&Spring Data JPA实现原理
2013-12-30 11:08
274 查看
因为我们最近的一个项目数据库访问呢层使用Ibatis, 今天团队成员问Ibatis中只写接口,不写实现,Ibatis是如何帮助我们查询数据。其实原理很简单,就是Java的反射和代理,因为Java的代理是真对于接口的。所以我们就可以在开发中DAO模块就直接写接口和对用的SQL就可以。实现类由我们生成代理,当代理方法被调用的时候我们就使用通用的数据库访问方法去接管它,为清晰简单的说明原理,这里直接给代码。
关键代码1
关键代码2
以上代码比较粗糙,只用于技术实现验证使用;我没有测试上面代码,感兴趣的农码可以试试,O(∩_∩)O哈哈~
[code=plain]public class User { private long id; private String name; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
[code=plain]@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Query { String value(); } @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Update { String value(); } @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Insert { String value(); } @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface Param { String value(); }
[code=plain]public interface UserDao { @Update("delete from t_user where id=:id") void delete(@Param("id")long id); @Insert("insert t_user(id,name) values(:id,:name)") long add(@Param("id")long id,@Param("name")String name); @Update("update t_user set name=:name where id=:id") boolean modify(@Param("name")String name,@Param("id") long id); @Query("Select * from t_user id=:id") User find(@Param("id")long id); }
关键代码1
[code=plain]import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author Silence */ public class DaoInvocationHandlerImpl implements InvocationHandler { private static Connection conn = null; static{ try { conn = DriverManager.getConnection("jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true", "test", "test"); } catch (SQLException ex) { Logger.getLogger(DaoInvocationHandlerImpl.class.getName()).log(Level.SEVERE, null, ex); } } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Logger.getLogger(DaoInvocationHandlerImpl.class.getName()).log(Level.SEVERE, "{0} invoke", method.getName()); Annotation[][] annos = method.getParameterAnnotations(); try { if (method.getAnnotation(Query.class) != null) { Query query = method.getAnnotation(Query.class); NamedParameterStatement nps = new NamedParameterStatement(conn, query.value()); for(int i=0;i<annos.length;i++){ for(Annotation an:annos[i]){ if(an instanceof Param){ nps.setObject(((Param)an).value(), args[i]); } } } //这里也可以再次包装 return nps.executeQuery(); } else if (method.getAnnotation(Insert.class) != null) { //代码省略 return null; } else if (method.getAnnotation(Update.class) != null){ //代码省略 return null; } else { //代码省略 return null; } } catch (Exception e) { Logger.getLogger(DaoInvocationHandlerImpl.class.getName()).log(Level.SEVERE, "error occurs in [" + method.toGenericString() + "]", e); throw e; } } }
[code=plain]//这段代码来自网上的主要用于命名参数,就是把sql中的参数替换成问号,并设置对应的值 import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * * @author Silence */ public class NamedParameterStatement { /** The statement this object is wrapping. */ private final PreparedStatement statement; /** Maps parameter names to arrays of ints which are the parameter indices. */ private final Map indexMap; /** * Creates a NamedParameterStatement. Wraps a call to * c.{@link Connection#prepareStatement(java.lang.String) prepareStatement}. * @param connection the database connection * @param query the parameterized query * @throws SQLException if the statement could not be created */ public NamedParameterStatement(Connection connection, String query) throws SQLException { indexMap=new HashMap(); String parsedQuery=parse(query, indexMap); statement=connection.prepareStatement(parsedQuery); } /** * Parses a query with named parameters. The parameter-index mappings are put into the map, and the * parsed query is returned. DO NOT CALL FROM CLIENT CODE. This method is non-private so JUnit code can * test it. * @param query query to parse * @param paramMap map to hold parameter-index mappings * @return the parsed query */ static final String parse(String query, Map paramMap) { // I was originally using regular expressions, but they didn't work well for ignoring // parameter-like strings inside quotes. int length=query.length(); StringBuffer parsedQuery=new StringBuffer(length); boolean inSingleQuote=false; boolean inDoubleQuote=false; int index=1; for(int i=0;i<length;i++) { char c=query.charAt(i); if(inSingleQuote) { if(c=='\'') { inSingleQuote=false; } } else if(inDoubleQuote) { if(c=='"') { inDoubleQuote=false; } } else { if(c=='\'') { inSingleQuote=true; } else if(c=='"') { inDoubleQuote=true; } else if(c==':' && i+1<length && Character.isJavaIdentifierStart(query.charAt(i+1))) { int j=i+2; while(j<length && Character.isJavaIdentifierPart(query.charAt(j))) { j++; } String name=query.substring(i+1,j); c='?'; // replace the parameter with a question mark i+=name.length(); // skip past the end if the parameter List indexList=(List)paramMap.get(name); if(indexList==null) { indexList=new LinkedList(); paramMap.put(name, indexList); } indexList.add(new Integer(index)); index++; } } parsedQuery.append(c); } // replace the lists of Integer objects with arrays of ints for(Iterator itr=paramMap.entrySet().iterator(); itr.hasNext();) { Map.Entry entry=(Map.Entry)itr.next(); List list=(List)entry.getValue(); int[] indexes=new int[list.size()]; int i=0; for(Iterator itr2=list.iterator(); itr2.hasNext();) { Integer x=(Integer)itr2.next(); indexes[i++]=x.intValue(); } entry.setValue(indexes); } return parsedQuery.toString(); } /** * Returns the indexes for a parameter. * @param name parameter name * @return parameter indexes * @throws IllegalArgumentException if the parameter does not exist */ private int[] getIndexes(String name) { int[] indexes=(int[])indexMap.get(name); if(indexes==null) { throw new IllegalArgumentException("Parameter not found: "+name); } return indexes; } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setObject(int, java.lang.Object) */ public void setObject(String name, Object value) throws SQLException { int[] indexes=getIndexes(name); for(int i=0; i < indexes.length; i++) { statement.setObject(indexes[i], value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setString(int, java.lang.String) */ public void setString(String name, String value) throws SQLException { int[] indexes=getIndexes(name); for(int i=0; i < indexes.length; i++) { statement.setString(indexes[i], value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setInt(int, int) */ public void setInt(String name, int value) throws SQLException { int[] indexes=getIndexes(name); for(int i=0; i < indexes.length; i++) { statement.setInt(indexes[i], value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setInt(int, int) */ public void setLong(String name, long value) throws SQLException { int[] indexes=getIndexes(name); for(int i=0; i < indexes.length; i++) { statement.setLong(indexes[i], value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setTimestamp(int, java.sql.Timestamp) */ public void setTimestamp(String name, Timestamp value) throws SQLException { int[] indexes=getIndexes(name); for(int i=0; i < indexes.length; i++) { statement.setTimestamp(indexes[i], value); } } /** * Returns the underlying statement. * @return the statement */ public PreparedStatement getStatement() { return statement; } /** * Executes the statement. * @return true if the first result is a {@link ResultSet} * @throws SQLException if an error occurred * @see PreparedStatement#execute() */ public boolean execute() throws SQLException { return statement.execute(); } /** * Executes the statement, which must be a query. * @return the query results * @throws SQLException if an error occurred * @see PreparedStatement#executeQuery() */ public ResultSet executeQuery() throws SQLException { return statement.executeQuery(); } /** * Executes the statement, which must be an SQL INSERT, UPDATE or DELETE statement; * or an SQL statement that returns nothing, such as a DDL statement. * @return number of rows affected * @throws SQLException if an error occurred * @see PreparedStatement#executeUpdate() */ public int executeUpdate() throws SQLException { return statement.executeUpdate(); } /** * Closes the statement. * @throws SQLException if an error occurred * @see Statement#close() */ public void close() throws SQLException { statement.close(); } /** * Adds the current set of parameters as a batch entry. * @throws SQLException if something went wrong */ public void addBatch() throws SQLException { statement.addBatch(); } /** * Executes all of the batched statements. * * See {@link Statement#executeBatch()} for details. * @return update counts for each statement * @throws SQLException if something went wrong */ public int[] executeBatch() throws SQLException { return statement.executeBatch(); } }
关键代码2
[code=plain]public class Test { public static void main(String[] args) { //如果使用spring,可以实现spring工厂bean接口包装这段代码,为server类提供注入 UserDao dao = (UserDao) Proxy.newProxyInstance( DaoInvocationHandlerImpl.class.getClassLoader(), new Class[]{UserDao.class}, new DaoInvocationHandlerImpl()); dao.find(1L); } }
以上代码比较粗糙,只用于技术实现验证使用;我没有测试上面代码,感兴趣的农码可以试试,O(∩_∩)O哈哈~
相关文章推荐
- 在Spring Boot中使用Spring-data-jpa实现分页查询
- 结合SpringDataJPA中的PagingAndSortingRepository和 JpaSpecificationExecutor接口实现大数据量的数据分页查询
- spring+ibatis实现DB的动态切换2(AbstractRoutingDataSource的原理)
- springdata jpa使用Example快速实现动态查询
- SpringBoot第二讲 利用Spring Data JPA实现数据库的访问(一)
- 在Spring Boot中使用Spring-data-jpa实现分页查询
- Spring Data JPA实现动态条件与范围查询实例代码
- 在Spring Boot中使用Spring-data-jpa实现分页查询
- SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法
- (十三)SpringBoot之Spring-Data-Jpa(二)CRUD实现以及添加自定义方法
- spring-data-jpa原理探秘(1)-运行环境创建及加载Repository接口
- Spring Data Jpa 实现分页(Spring MVC+easyui)
- 【spring data jpa】带有条件的查询后分页和不带条件查询后分页实现
- Spring Data JPA(2)--利用PagingAndSortingRespository实现分页和排序
- SpringMVC+Spring Data JPA实现增删改查操作
- 在Spring Boot中使用Spring-data-jpa实现分页查询
- SpringBoot第二讲利用Spring Data JPA实现数据库的访问(一)
- 在Spring Boot中使用Spring-data-jpa实现分页查询(转)
- 【spring boot】9.spring boot+spring-data-jpa的入门使用,实现数据持久化
- SpringBoot使用Spring-Data-Jpa实现CRUD操作