<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

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    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) 評論(2)  編輯  收藏 所屬分類: Database Access Layer & Persistence Layer

    FeedBack:
    # re: [絕對原創] 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 !  回復  更多評論
      
    # re: [絕對原創] Simplify Duplicated JDBC Code By Annotation - How can we remove Connection, Statement, and ResultSet Codes from DAO method 2009-03-23 11:15 545
    mark  回復  更多評論
      

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲国产精品免费观看| 亚洲一区视频在线播放| 中国在线观看免费国语版| 一级毛片全部免费播放| 国产精品网站在线观看免费传媒| 精品一区二区三区高清免费观看| 一级黄色片免费观看| 一级做a爰片性色毛片免费网站 | 国产午夜鲁丝片AV无码免费| 真实乱视频国产免费观看| 日本二区免费一片黄2019| 国产又大又长又粗又硬的免费视频| 国产高清免费的视频| 国产免费午夜a无码v视频| 国产99视频免费精品是看6| 免费又黄又硬又爽大片| 中文字幕亚洲专区| 亚洲国产综合无码一区 | 卡一卡二卡三在线入口免费| 精品久久久久成人码免费动漫| 永久免费毛片在线播放| 午夜毛片不卡免费观看视频| 日本免费人成黄页网观看视频| 国产91在线免费| 国外亚洲成AV人片在线观看| 亚洲av午夜成人片精品网站| 亚洲电影在线播放| 亚洲日本天堂在线| 一级毛片免费在线播放| 国产午夜成人免费看片无遮挡| 少妇人妻偷人精品免费视频| 97视频热人人精品免费| 免费A级毛片无码久久版| 亚洲中文字幕无码一区| 亚洲天天做日日做天天看| 亚洲AV无码一区二区三区在线 | 亚洲啪啪综合AV一区| 亚洲国产日韩在线一区| 噜噜综合亚洲AV中文无码| 久久九九免费高清视频| **一级一级毛片免费观看|