文档章节

IBatis Mapper&Spring Data JPA实现原理

xdev
 xdev
发布于 2013/04/28 04:09
字数 1461
阅读 4237
收藏 24
点赞 2
评论 4

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

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;
    }
    
}
@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();
}
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

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;
        }
    }
    
}
//这段代码来自网上的主要用于命名参数,就是把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

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哈哈~

© 著作权归作者所有

共有 人打赏支持
xdev
粉丝 32
博文 15
码字总数 6706
作品 1
广州
架构师
加载中

评论(4)

xdev
xdev

引用来自“魅影骑士360”的评论

AopInvocationHandlerImpl是哪里的啊??
恩,手写错误已经改成DaoInvocationHandlerImpl
魅影骑士360
魅影骑士360
AopInvocationHandlerImpl是哪里的啊??
令飞
令飞
想想也是这样,通过接口名称一定是反射
fair_jm
fair_jm
原来没想过 最近看到spring的JPA的时候就觉得好奇 谢谢po主的分享 ^_^
成为Java高手的25个学习目标

你已经掌握了这25条中的多少条了呢? 1.你需要精通面向对象分析与设计(OOA/OOD)、涉及模式(GOF,J2EEDP)以及综合模式。你应该了解UML,尤其是 class,object,interaction以及statediag...

thinkyoung ⋅ 2014/11/01 ⋅ 0

JSP 学习总结---学习笔记

什么是JSP 1)为什么说,Servlet是一个动态Web开发技术呢? Servlet是基于服务端的一种动态交互技术, HttpServletRequest表示客户端到服务端的对象 HttpServletResponse表示服务端到客户端的...

知止内明 ⋅ 04/18 ⋅ 0

Servlet的一些细节--学习笔记

Servlet细节 1)浏览器访问的url-pattern只是一个符合格式的任意字符串,以/开头 2)一个Servlet的url-pattern可以是1个或多个,有二种形式; a).xx b)/xx/ 注意:/不能一起直接使用 3)/和....

知止内明 ⋅ 04/17 ⋅ 0

spring data jpa 详解

本篇进行Spring-data-jpa的介绍,几乎涵盖该框架的所有方面,在日常的开发当中,基本上能满足所有需求。这里不讲解JPA和Spring-data-jpa单独使用,所有的内容都是在和Spring整合的环境中实现...

无敌小学僧 ⋅ 05/30 ⋅ 0

jpa----spring-data-jpa 复杂查询,包括多表关联,分页,排序等

转载:由于此转载文章的出处也是转载的,所以原作者不详 本篇进行Spring-data-jpa的介绍,几乎涵盖该框架的所有方面,在日常的开发当中,基本上能满足所有需求。这里不讲解JPA和Spring-data...

u010775025 ⋅ 05/29 ⋅ 0

Spring mvc 创建完项目测试的时候前台404,后台没有错误信息这是咋了

前天遇到一个问题,springmvc创建的项目测试的时候前台404,后台没有错误输出,奇了怪了...... 这是WEB-INF下的xml文件

好吧我输了 ⋅ 前天 ⋅ 0

JavaWeb开发比较重要的面试题

JavaWeb开发比较重要的面试题 1. 编码转换:怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串? 答:示例代码如下: String s1=”你好”; String s2=new String(s1.getBytes(“GB2312”...

xj_9264 ⋅ 05/06 ⋅ 0

Java Web(一) Servlet详解!!

一、什么是servlet?     处理请求和发送响应的过程是由一种叫做Servlet的程序来完成的,并且Servlet是为了解决实现动态页面而衍生的东西。理解这个的前提是了解一些http协议的东西,并且...

architect刘源源 ⋅ 05/08 ⋅ 0

Servlet创建运行的过程--生命周期以及自动加载

前言 初学者像我这样的,刚开始学的时候一脸懵逼,为什么Servlet可以直接运行,明明没有入口啊 只是重写了Servlet中的方法。然后访问该地址怎么就直接运行了。 所以了解Serlvet的运行过程即生...

codingcoge ⋅ 06/12 ⋅ 0

web.xml中在Servlet中获取context-param和init-param内的参数

web.xml里面可以定义两种参数: 1.application范围内的参数,存放在servletcontext中,在web.xml中配置如下: <context-param> <param-name>context/param</param-name> <param-value>avalib......

村长大神 ⋅ 2014/10/27 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

火狐浏览器各版本下载及插件httprequest

各版本下载地址:http://ftp.mozilla.org/pub/mozilla.org//firefox/releases/ httprequest插件截至57版本可用

xiaoge2016 ⋅ 10分钟前 ⋅ 0

Java学习路径及练手项目合集

Java学习路径及练手项目合集

颖伙虫 ⋅ 26分钟前 ⋅ 0

Docker系列教程28-实战:使用Docker Compose运行ELK

原文:http://www.itmuch.com/docker/28-docker-compose-in-action-elk/,转载请说明出处。 ElasticSearch【存储】 Logtash【日志聚合器】 Kibana【界面】 答案: version: '2'services: ...

周立_ITMuch ⋅ 51分钟前 ⋅ 0

使用快嘉sdkg极速搭建接口模拟系统

在具体项目研发过程中,一旦前后端双方约定好接口,前端和app同事就会希望后台同事可以尽快提供可供对接的接口方便调试,而对后台同事来说定好接口还仅是个开始、设计流程,实现业务逻辑,编...

fastjrun ⋅ 今天 ⋅ 0

PXE/KickStart 无人值守安装

导言 作为中小公司的运维,经常会遇到一些机械式的重复工作,例如:有时公司同时上线几十甚至上百台服务器,而且需要我们在短时间内完成系统安装。 常规的办法有什么? 光盘安装系统 ===> 一...

kangvcar ⋅ 昨天 ⋅ 0

使用Puppeteer撸一个爬虫

Puppeteer是什么 puppeteer是谷歌chrome团队官方开发的一个无界面(Headless)chrome工具。Chrome Headless将成为web应用自动化测试的行业标杆。所以我们很有必要来了解一下它。所谓的无头浏...

小草先森 ⋅ 昨天 ⋅ 0

Java Done Right

* 表示难度较大或理论性较强。 ** 表示难度更大或理论性更强。 【Java语言本身】 基础语法,面向对象,顺序编程,并发编程,网络编程,泛型,注解,lambda(Java8),module(Java9),var(...

风华神使 ⋅ 昨天 ⋅ 0

Linux系统日志

linux 系统日志 /var/log/messages /etc/logrotate.conf 日志切割配置文件 https://my.oschina.net/u/2000675/blog/908189 logrotate 使用详解 dmesg 命令 /var/log/dmesg 日志 last命令,调......

Linux学习笔记 ⋅ 昨天 ⋅ 0

MVC——统一报文格式的异常处理响应

在我们写controller层的时候,常常会有这样的困惑,如果需要返回一个数据是,可能为了统一回去构造一个类似下列的数据格式: { status:true, msg:"保存成功!", data:[]} 而且在写...

alexzhu592 ⋅ 昨天 ⋅ 0

android -------- 打开本地浏览器或指定浏览器加载,打电话,打开第三方app

开发中常常有打开本地浏览器加载url或者指定浏览器加载, 还有打开第三方app, 如 打开高德地图 百度地图等 在Android程序中我们可以通过发送隐式Intent来启动系统默认的浏览器。 如果手机本身...

切切歆语 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部