<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 41  文章 - 29  trackbacks - 0
    <2009年3月>
    22232425262728
    1234567
    891011121314
    15161718192021
    22232425262728
    2930311234

    常用鏈接

    留言簿(5)

    隨筆分類(28)

    隨筆檔案(23)

    收藏夾(6)

    Inside JVM

    Java

    java performance

    Solr

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    JDBC has been used as the major Java Database Access technique for long time. However, JDBC access always need try-catch block and repeated JDBC codes, such as connection, statement and resultset.
    And recently, JDK 6.0 already released a simplified JDBC access approach. This article will introduce a similar and very simple design to reduce most of JDBC codes

    1. Typical JDBC Code Template

    A typical JDBC code template is as following -
     1     public void example() {
     2 
     3         Connection connection = null;
     4         PreparedStatement cstmt = null;
     5         ResultSet resultSet = null;
     6 
     7         try {
     8             connection = AccessUtil.getInstance().getConnection();
     9             cstmt = connection.prepareStatement("SELECT * FROM VENDOR");
    10             // cstmt.setString(1, getIdString(hotelIds, ","));
    11             cstmt.executeQuery();
    12             resultSet = cstmt.getResultSet();
    13             // handle result set
    14             while (resultSet.next()) {
    15                  //get result one by one
    16             }
    17 
    18         } catch (SQLException e) {
    19             log.error(e.getMessage(), e);
    20             throw new CommonSearchRuntimeExcetion(e);
    21         } finally {
    22             try {
    23                 if (resultSet != null) {
    24                     resultSet.close();
    25                 }
    26             } catch (Exception e) {
    27                 /* swallow */
    28             }
    29 
    30             try {
    31                 if (cstmt != null) {
    32                     cstmt.close();
    33                 }
    34             } catch (Exception e) {
    35                 /* swallow */
    36             }
    37 
    38             try {
    39                 if (connection != null) {
    40                     connection.close();
    41                 }
    42             } catch (Exception e) {
    43                 /* swallow */
    44             }
    45         }
    46     }
    As you see in the above example, actually, we only need execute one query "SELECT * FROM VEDNOR", however, we used 46 lines of codes to execute this query and most of codes are exactly same among different access method. It caused big block mess codes.
    We need remove them.

    2. Use Annotation & Proxy to Remove most of repeated JDBC codes

    Let's think about what are the necessary information to execute a query.
    1. the SQL to be executed
    2. the SQL parameters
    3. because of JDBC update and query need be handled separated, we also need know it is update or select operation
    So, the design could be
    1. a Method annotation to get SQL statement and operation type (udpate including insert or select)
    2. an InvocationHandler (proxy) to execute the query behind the DAO interface

    Method Annotation Implementation

     1 @Target(ElementType.METHOD)
     2 @Retention(RetentionPolicy.RUNTIME)
     3 public @interface DataAccessor {
     4 
     5     
     6     /**
     7      * query string
     8      * NOTE: the query string can be "SELECT * FROM VS_VendorMeetingRooms WHERE vendorUniqueID in (??)"
     9      * However, if it is collection value, need use "??" instead of "?"
    10      */
    11     String query() default "";
    12     
    13     /**
    14      * Data operation type 
    15      * @return
    16      */
    17     DataOperationType type() default DataOperationType.SELECT_OPERATION; 
    18     
    19 
    20 }

    The annotation is very easy to understand, however, here is just one thing. Because JDBC cannot directly set collection parameters, so, if we want to set an collection parameter, we have to do a little tricky thing. So, the SQL used "??" to represent a collection parameter. You can see the example on how to use it

    Parameter Annotation Example 

    1 @Target(ElementType.PARAMETER)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface ParamIndex {
    4     
    5     int index();
    6     
    7 }

    InvocationHandler Implementation Example

      1 public class DataAccessorInvocationHandler implements InvocationHandler {
      2 
      3     /**
      4      * Just sits here to hold onto the representation of the finder method so we
      5      * don't have to setup the context each time.
      6      */
      7     private static final Map<String, DataAccessorDescriptor> finderCache = new WeakHashMap<String, DataAccessorDescriptor>();
      8     
      9     private static final Log log = LogFactory.getLog(DataAccessorInvocationHandler.class);
     10 
     11 
     12     /**
     13      * This is the basic method interceptor that the proxy builds on.
     14      */
     15     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     16 
     17        
     18         DataAccessorDescriptor descriptor = null;
     19         RowSetDynaClass rowSetDynaClass = null;
     20         try {
     21             
     22             // obtain a cached finder descriptor (or create a new one)
     23             descriptor = getFinderDescriptor(method);
     24 
     25             createConnection(descriptor);
     26             
     27             //fill parameters 
     28             buildPreparedStatement(descriptor, args);
     29             
     30             //get the result
     31             rowSetDynaClass = createReturnResult(descriptor);
     32             
     33         } finally {
     34             
     35             if (descriptor != null) {
     36                 
     37                 ResultSet rs = descriptor.resultSet;
     38                 PreparedStatement pstmt = descriptor.statement;
     39                 Connection dbConn = descriptor.connection;
     40                 
     41                 try {
     42                     if (rs != null) {
     43                         rs.close();
     44                     }
     45                 } catch (Exception e) {
     46                     log.error(e.getMessage(), e);
     47                     throw new CommonSearchRuntimeException(e);
     48                 }
     49 
     50                 
     51                 try {
     52                     if (pstmt != null) {
     53                         pstmt.close();
     54                     }
     55                 } catch (Exception e) {
     56                     log.error(e.getMessage(), e);
     57                     throw new CommonSearchRuntimeException(e);
     58                 }
     59 
     60                 try {
     61                     if (dbConn != null) {
     62                         dbConn.close();
     63                     }
     64                 } catch (Exception e) {
     65                     log.error(e.getMessage(), e);
     66                     throw new CommonSearchRuntimeException(e);
     67                 }
     68             }
     69             
     70            
     71            
     72         }
     73         return rowSetDynaClass;
     74     }
     75 
     76     /**
     77      * Creates return result
     78      * @param pstmt
     79      * @param descriptor
     80      * @return
     81      * @throws SQLException
     82      * @throws IllegalAccessException 
     83      * @throws InstantiationException 
     84      */
     85     private RowSetDynaClass createReturnResult(DataAccessorDescriptor descriptor)
     86          throws SQLException, InstantiationException, IllegalAccessException {
     87 
     88         PreparedStatement statement = descriptor.statement;
     89 
     90         if (DataOperationType.SELECT_OPERATION.equals(descriptor.operationType)) {
     91             
     92             ResultSet rs = statement.executeQuery();
     93             
     94             RowSetDynaClass rowSetDynaClass = new RowSetDynaClass(rs);
     95 
     96             return rowSetDynaClass;
     97         } else if (DataOperationType.UPDATE_OPERATION.equals(descriptor.operationType)) {
     98             statement.executeUpdate();
     99             return null;
    100         }
    101         
    102         return null;
    103     }
    104 
    105     /**
    106      * 
    107      * @param descriptor
    108      */
    109     private void createConnection(DataAccessorDescriptor descriptor) {
    110         descriptor.connection = AccessUtil.getInstance().getConnection();
    111     }
    112     
    113     
    114     /**
    115      * This method will handle the binding of named parameters
    116      * 
    117      * @param hibernateQuery
    118      * @param descriptor
    119      * @param arguments
    120      * @throws SQLException 
    121      */
    122     private void buildPreparedStatement(DataAccessorDescriptor descriptor, 
    123             Object[] arguments) throws SQLException {
    124         
    125         class SQLParameter {
    126             //0 = collection parameter, which means the "?" will be replaced by a string, 
    127             //1 = normal JDBC parameter, use PreparedStatement.setObject to set parameters
    128             int type; 
    129             Object value;
    130         };
    131         
    132         //ordered parameter map, the 
    133         SQLParameter[] parameters = null;
    134         if (arguments == null) {
    135             parameters = new SQLParameter[0]; 
    136         } else {
    137             parameters = new SQLParameter[arguments.length]; 
    138             for (int i = 0; i < arguments.length; i++) {
    139                 Object argument = arguments[i];
    140                 Object annotation = descriptor.parameterAnnotations[i];
    141 
    142                 if (null == annotation){
    143                     continue// skip param as it's not bindable
    144                 } else if (annotation instanceof ParamIndex) {
    145                     
    146                     ParamIndex param = (ParamIndex) annotation;
    147                     SQLParameter parameter = new SQLParameter();
    148                     //if collection, we have to build Query string by ourselves
    149                     if (argument instanceof Collection) {
    150                         Collection<?> collection = (Collection<?>)argument;
    151                         parameter.type = 0;
    152                         parameter.value = StringAppendUtil.buildStringSmartly(collection, ",");
    153                     } else {
    154                         parameter.type = 1;
    155                         parameter.value = argument;
    156                     }
    157                     parameters[param.index() - 1= parameter;
    158                 }
    159             }
    160         }
    161         
    162         //firstly, replace all collection parameters by string value
    163         if (parameters.length > 0) {
    164             for (int i = 0; i < parameters.length; i++) {
    165                 SQLParameter parameter = parameters[i];
    166                 if (parameter.type == 0) {
    167                     descriptor.query = descriptor.query.replaceFirst("\\?\\?", (String)parameter.value);
    168                 } 
    169             }
    170         }
    171         //secondly, create statement
    172         descriptor.statement = descriptor.connection.prepareStatement(descriptor.query);
    173         //finally, fill parameters
    174         if (parameters.length > 0) {
    175             int index = 1;
    176             for (int i = 0; i < parameters.length; i++) {
    177                 SQLParameter parameter = parameters[i];
    178                 if (parameter.type == 1) {
    179                     descriptor.statement.setObject(index, parameter.value);
    180                     index++;
    181                 } 
    182             }
    183         }
    184     }
    185 
    186     /**
    187      * This is the method that goes ahead and looks at the method to create the
    188      * descriptor of it.
    189      * 
    190      * @param method
    191      * @return
    192      */
    193     private DataAccessorDescriptor getFinderDescriptor(Method method) {
    194         
    195         String messageKey = getMethodKey(method);
    196         
    197         DataAccessorDescriptor descriptor = finderCache.get(messageKey);
    198         if (null != descriptor) {
    199             return descriptor;
    200         }
    201         
    202         // otherwise reflect and cache finder info
    203         descriptor = new DataAccessorDescriptor();
    204         finderCache.put(messageKey, descriptor);
    205 
    206         DataAccessor accessor = method.getAnnotation(DataAccessor.class);
    207         String query = accessor.query();
    208         if (!"".equals(query.trim())) {
    209             descriptor.query = query;
    210         }
    211         
    212         descriptor.operationType = accessor.type();
    213         
    214         // determine parameter annotations
    215         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    216         Object[] discoveredAnnotations = new Object[parameterAnnotations.length];
    217         for (int i = 0; i < parameterAnnotations.length; i++) {
    218             Annotation[] annotations = parameterAnnotations[i];
    219             // each annotation per param
    220             for (Annotation annotation : annotations) {
    221                 Class<? extends Annotation> annotationType = annotation.annotationType();
    222                 if (ParamIndex.class.equals(annotationType)) {
    223                     discoveredAnnotations[i] = annotation;
    224                     //finderDescriptor.isBindAsRawParameters = false;
    225                     break;
    226                 } 
    227             }
    228         }
    229 
    230         // set the discovered set to our finder cache object
    231         descriptor.parameterAnnotations = discoveredAnnotations;
    232 
    233         return descriptor;
    234     }
    235     
    236     /**
    237      * Constructs a String key by method name and parameters 
    238      * @param method
    239      * @return
    240      */
    241     private String getMethodKey(Method method){
    242         StringBuilder methodString = new StringBuilder();
    243         methodString.append(method.getName());
    244         for(Class<?> clazz: method.getParameterTypes()){
    245             methodString.append(clazz.getName());
    246         }
    247         return methodString.toString();
    248     }
    249 
    250     /**
    251      * A simple class that is used in the cache for information about a finder
    252      * method.
    253      */
    254     private static class DataAccessorDescriptor {
    255         
    256         Object[] parameterAnnotations;
    257         
    258         //the query string to be executed  
    259         String query;
    260         
    261         DataOperationType operationType = DataOperationType.SELECT_OPERATION; 
    262 
    263         Connection connection = null;
    264         
    265         PreparedStatement statement = null;
    266         
    267         ResultSet resultSet = null;
    268     }
    269     
    270 }

    NOTE:
    (1) for every JDBC call, we will always return RowSetDynaClass, which is one dynamic java bean implementation provided by Apache Common BeanUtils lib. In this way, we don't need any complicated O-R mapping tool.
    (2) Collection parameters such as the parameters in "in(??)" and single parameter are handled seperatedly, you will see the example as following.

    DatabaseAccessor Example

    Now, the implementation of DatabaseAccssor will be very simple and straight-forward.
     
     1 public interface VendorAccessor {
     2         //SELECT example
     3     @DataAccessor(query =  "SELECT i.* FROM VS_Image i WHERE i.vendorID in (??)")
     4     public RowSetDynaClass getVendorImage(@ParamIndex(index = 1) List<Long> vendorIds);
     5 
     6         //UPDATE example
     7     @DataAccessor(query = "update VS_VendorSearch set changeFlag = ?",
     8             type = DataOperationType.UPDATE_OPERATION)
     9     public void updateChangeFlagAsTrueForVendorSearch(@ParamIndex(index = 1) String changeFlag);
    10 }

    DatabaseAccessorFactory Example


     1 public class DataAccessorFactory {
     2 
     3     private static final Map<Class<?>, Object> proxyCache = new HashMap<Class<?>, Object>();
     4     
     5     /**
     6      * Create proxy for accessor
     7      * @param accessor
     8      * @return
     9      */
    10     public synchronized static Object getInstance(Class<?> accessor) {
    11 
    12         Object accessorProxy = null;
    13         if ((accessorProxy = proxyCache.get(accessor)) == null) {
    14             accessorProxy = Proxy.newProxyInstance(DataAccessorInvocationHandler.class.getClassLoader(), 
    15                     new Class[] {accessor}, new DataAccessorInvocationHandler());
    16             proxyCache.put(accessor, accessorProxy);
    17         }
    18 
    19         return accessorProxy;
    20     }
    21     
    22 }

    Accessor Client Example

        private static final ExampleAccessor accessor = (ExampleAccessor)DataAccessorFactory.getInstance(ExampleAccessor.class);
        public List<Vendor> getVendors(Boolean needAll) {
            List
    <Vendor> vendors = new ArrayList<Vendor>();
            
            RowSetDynaClass beans  
    = accessor.getVendors();

            
    for(Object o : beans.getRows()) {
                DynaBean bean  
    = (DynaBean) o;
                Vendor v 
    = new Vendor();
                v.setID((Long)getProperty(bean, 
    "id"));
            
                
                purchasedVendors.add(v);
            }
            
            
    return purchasedVendors;
        }

    As you see, the client is very clean and straight-forward, there is no "try-catch, connection, statement, result set" etc any more.

    3. Why we need this design

    Let me summarize it again
    • for the DAO class (accessor), it is very clean, such as
     1 public interface VendorAccessor {
     
    2         //SELECT example
     3     @DataAccessor(query =  "SELECT i.* FROM VS_Image i WHERE i.vendorID in (??)")
     
    4     public RowSetDynaClass getVendorImage(@ParamIndex(index = 1) List<Long> vendorIds);
     
    5 
     
    6         //UPDATE example
     7     @DataAccessor(query = "update VS_VendorSearch set changeFlag = ?",
     
    8             type = DataOperationType.UPDATE_OPERATION)
     
    9     public void updateChangeFlagAsTrueForVendorSearch(@ParamIndex(index = 1) String changeFlag);
    10 }
    • for the DAO class client, the usage on DAO class is also very clean and straight-forwar. such as 

     1     private static final ExampleAccessor accessor = (ExampleAccessor)DataAccessorFactory.getInstance(ExampleAccessor.class);
     2     public List<Vendor> getVendors(Boolean needAll) {
     3         List<Vendor> vendors = new ArrayList<Vendor>();
     4         
     5         RowSetDynaClass beans  = accessor.getVendors();
     6 
     7         for(Object o : beans.getRows()) {
     8             DynaBean bean  = (DynaBean) o;
     9             Vendor v = new Vendor();
    10             v.setID((Long)getProperty(bean, "id"));
    11         
    12             
    13             purchasedVendors.add(v);
    14         }
    15         
    16         return purchasedVendors;
    17     }
    Any comment, please share with me .

    posted on 2009-03-20 10:07 Justin Chen 閱讀(2010) 評(píng)論(2)  編輯  收藏 所屬分類: Database Access Layer & Persistence Layer

    FeedBack:
    # re: [絕對(duì)原創(chuàng)] Simplify Duplicated JDBC Code By Annotation - How can we remove Connection, Statement, and ResultSet Codes from DAO method 2009-03-20 19:37 super2
    splendid !  回復(fù)  更多評(píng)論
      
    # re: [絕對(duì)原創(chuàng)] Simplify Duplicated JDBC Code By Annotation - How can we remove Connection, Statement, and ResultSet Codes from DAO method 2009-03-23 11:15 545

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 91成人在线免费视频| 国产亚洲欧洲精品| 亚洲首页国产精品丝袜| 暖暖在线视频免费视频| 久99精品视频在线观看婷亚洲片国产一区一级在线 | 美女黄网站人色视频免费国产| 亚洲成人在线免费观看| 久久久久成人片免费观看蜜芽| 亚洲桃色AV无码| 国产免费久久精品99久久| 免费v片在线观看无遮挡| 亚洲av无码偷拍在线观看| 成年女人看片免费视频播放器| 亚洲性69影院在线观看| 精品免费久久久久久久| 亚洲精品福利视频| 性无码免费一区二区三区在线| 国产亚洲综合网曝门系列| 香蕉视频在线免费看| 亚洲精品第一国产综合精品99| 精品国产日韩亚洲一区91| 全免费一级午夜毛片| 亚洲精品无码成人| 精品免费国产一区二区三区| 亚洲欧洲专线一区| 精品久久久久久久免费人妻| 亚洲色欲啪啪久久WWW综合网| 18禁无遮挡无码网站免费| 国产成人亚洲综合网站不卡| 无码少妇一区二区浪潮免费| 亚洲av永久无码嘿嘿嘿| 毛片免费观看视频| 欧美色欧美亚洲另类二区| 国产成人高清精品免费鸭子| 国产成人亚洲综合a∨| 免费A级毛片无码久久版| 无套内射无矿码免费看黄| 亚洲精品无码你懂的网站| 久久免费香蕉视频| 亚洲AV无码一区二区乱孑伦AS| 无码人妻精品中文字幕免费|