<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 閱讀(2023) 評論(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  回復  更多評論
      

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


    網站導航:
     
    主站蜘蛛池模板: 久久久精品国产亚洲成人满18免费网站| 最近中文字幕免费2019| 国产精品免费看香蕉| 亚洲精品V天堂中文字幕| 国产精品久久免费视频| 亚洲中文字幕久久久一区| 日韩精品福利片午夜免费观着| 亚洲影视自拍揄拍愉拍| 最近中文字幕无免费视频| 亚洲精品国产首次亮相 | 热99re久久精品精品免费| 亚洲午夜精品第一区二区8050| 日本亚洲欧美色视频在线播放| 国产美女精品久久久久久久免费| 亚洲av成人片在线观看| 夜色阁亚洲一区二区三区| 亚洲欧洲日韩综合| 国产人在线成免费视频| 亚洲乱妇熟女爽到高潮的片| 午夜小视频免费观看| 免费大片av手机看片| 女人与禽交视频免费看| 亚洲av乱码中文一区二区三区| 日韩亚洲精品福利| 最近更新免费中文字幕大全| 免费萌白酱国产一区二区| 久久丫精品国产亚洲av不卡| 美女裸身网站免费看免费网站| 亚洲国产成人久久综合| 国产啪亚洲国产精品无码| 免费视频成人片在线观看| 亚洲中文久久精品无码1| 免费国产一级特黄久久| 久久精品国产免费| 亚洲va久久久噜噜噜久久狠狠| 永久免费无码网站在线观看个| 亚洲电影一区二区三区| 日韩免费在线视频| 亚洲色中文字幕在线播放| 自拍偷自拍亚洲精品被多人伦好爽| 午夜免费福利小电影|