OSDN Git Service

DBFlute-0.9.3に更新
[ea2ddl/ea2ddl.git] / ea2ddl-dao / src / main / java / jp / sourceforge / ea2ddl / dao / allcommon / s2dao / S2DaoMetaDataExtension.java
1 package jp.sourceforge.ea2ddl.dao.allcommon.s2dao;\r
2 \r
3 import java.lang.reflect.Method;\r
4 import java.lang.reflect.ParameterizedType;\r
5 import java.lang.reflect.Type;\r
6 import java.sql.Connection;\r
7 import java.sql.DatabaseMetaData;\r
8 import java.sql.PreparedStatement;\r
9 import java.sql.ResultSet;\r
10 import java.sql.SQLException;\r
11 import java.sql.Statement;\r
12 import java.util.ArrayList;\r
13 import java.util.List;\r
14 \r
15 import javax.sql.DataSource;\r
16 \r
17 import org.seasar.dao.BeanEnhancer;\r
18 import org.seasar.dao.BeanMetaData;\r
19 import org.seasar.dao.BeanMetaDataFactory;\r
20 import org.seasar.dao.ColumnNaming;\r
21 import org.seasar.dao.PropertyTypeFactoryBuilder;\r
22 import org.seasar.dao.RelationPropertyTypeFactoryBuilder;\r
23 import org.seasar.dao.RelationRowCreator;\r
24 import org.seasar.dao.RowCreator;\r
25 import org.seasar.dao.SqlCommand;\r
26 import org.seasar.dao.TableNaming;\r
27 import org.seasar.dao.dbms.DbmsManager;\r
28 import org.seasar.dao.impl.BeanMetaDataImpl;\r
29 import org.seasar.dao.impl.DaoMetaDataImpl;\r
30 import org.seasar.dao.impl.ResultSetHandlerFactoryImpl;\r
31 import org.seasar.dao.impl.SelectDynamicCommand;\r
32 import org.seasar.dao.impl.UpdateAutoStaticCommand;\r
33 import org.seasar.extension.jdbc.ResultSetHandler;\r
34 import org.seasar.extension.jdbc.StatementFactory;\r
35 import org.seasar.extension.jdbc.ValueType;\r
36 import org.seasar.extension.jdbc.types.ValueTypes;\r
37 import org.seasar.framework.beans.MethodNotFoundRuntimeException;\r
38 import org.seasar.framework.beans.factory.BeanDescFactory;\r
39 import org.seasar.framework.util.MethodUtil;\r
40 \r
41 import jp.sourceforge.ea2ddl.dao.allcommon.BehaviorSelector;\r
42 import jp.sourceforge.ea2ddl.dao.allcommon.Entity;\r
43 import jp.sourceforge.ea2ddl.dao.allcommon.annotation.OutsideSql;\r
44 import jp.sourceforge.ea2ddl.dao.allcommon.cbean.ConditionBean;\r
45 import jp.sourceforge.ea2ddl.dao.allcommon.cbean.ConditionBeanContext;\r
46 import jp.sourceforge.ea2ddl.dao.allcommon.cbean.outsidesql.OutsideSqlContext;\r
47 import jp.sourceforge.ea2ddl.dao.allcommon.dbmeta.DBMeta;\r
48 import jp.sourceforge.ea2ddl.dao.allcommon.exception.EntityAlreadyDeletedException;\r
49 import jp.sourceforge.ea2ddl.dao.allcommon.exception.BatchEntityAlreadyUpdatedException;\r
50 import jp.sourceforge.ea2ddl.dao.allcommon.exception.EntityDuplicatedException;\r
51 import jp.sourceforge.ea2ddl.dao.allcommon.jdbc.CursorHandler;\r
52 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.rshandler.InternalBeanListMetaDataResultSetHandler;\r
53 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.rshandler.InternalBeanArrayMetaDataResultSetHandler;\r
54 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalDeleteAutoStaticCommand;\r
55 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalDeleteBatchAutoStaticCommand;\r
56 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalDeleteQueryAutoDynamicCommand;\r
57 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalInsertAutoDynamicCommand;\r
58 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalInsertBatchAutoStaticCommand;\r
59 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalProcedureCommand;\r
60 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalUpdateAutoDynamicCommand;\r
61 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalUpdateBatchAutoStaticCommand;\r
62 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalUpdateDynamicCommand;\r
63 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalUpdateModifiedOnlyCommand;\r
64 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlcommand.InternalUpdateQueryAutoDynamicCommand;\r
65 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlhandler.InternalBasicHandler.SQLExceptionHandler;\r
66 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlhandler.InternalUpdateBatchAutoHandler;\r
67 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.sqlhandler.InternalDeleteBatchAutoHandler;\r
68 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.various.InternalProcedureMetaData;\r
69 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.various.InternalProcedureMetaDataFactory;\r
70 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.various.InternalRowCreator;\r
71 import jp.sourceforge.ea2ddl.dao.allcommon.s2dao.internal.various.InternalRelationRowCreator;\r
72 import jp.sourceforge.ea2ddl.dao.allcommon.util.SimpleSystemUtil;\r
73 \r
74 /**\r
75  * The extension of DaoMetaDataImpl for DBFlute.\r
76  * @author DBFlute(AutoGenerator)\r
77  */
78 @SuppressWarnings("unchecked")\r
79 public class S2DaoMetaDataExtension extends DaoMetaDataImpl {\r
80 \r
81     // ===================================================================================\r
82     //                                                                          Definition\r
83     //                                                                          ==========\r
84     /** Log instance. */\r
85     private static final org.apache.commons.logging.Log _log = org.apache.commons.logging.LogFactory.getLog(S2DaoMetaDataExtension.class);\r
86     \r
87     // ===================================================================================\r
88     //                                                                           Attribute\r
89     //                                                                           =========\r
90     /** Bean enhancer. */\r
91     protected BeanEnhancer beanEnhancer;\r
92 \r
93     /** The factory of annotation reader. */\r
94     protected org.seasar.dao.AnnotationReaderFactory annotationReaderFactory;\r
95 \r
96     /** The naming of column. {After S2Dao-1.0.47} */\r
97     protected ColumnNaming columnNaming;\r
98 \r
99     /** The builder of property type factory. {After S2Dao-1.0.47} */\r
100     protected PropertyTypeFactoryBuilder propertyTypeFactoryBuilder;\r
101 \r
102     /** The builder of relation property type factory. {After S2Dao-1.0.47} */\r
103     protected RelationPropertyTypeFactoryBuilder relationPropertyTypeFactoryBuilder;\r
104 \r
105     /** The builder of table naming. {After S2Dao-1.0.47} */\r
106     protected TableNaming tableNaming;\r
107 \r
108     // -----------------------------------------------------\r
109     //                                     DBFlute Extension\r
110     //                                     -----------------\r
111     /** The selector of behavior. {Since DBFlute-0.7.1} */\r
112     protected BehaviorSelector _behaviorSelector;\r
113     \r
114     /** The lock monitor of method initialization. */\r
115     protected Object _methodInitializationLockMonitor = new Object();\r
116     \r
117     /** The determination of internal debug. {Since DBFlute-0.6.2} */\r
118     protected boolean _internalDebug;\r
119     \r
120     // ===================================================================================\r
121     //                                                                         Constructor\r
122     //                                                                         ===========\r
123     public S2DaoMetaDataExtension() {\r
124     }\r
125 \r
126     // ===================================================================================\r
127     //                                                                 Initialize Override\r
128     //                                                                 ===================\r
129     @Override\r
130     public void initialize() {\r
131         beanClass = daoAnnotationReader.getBeanClass();\r
132         daoInterface = getDaoInterface(daoClass);\r
133         daoBeanDesc = BeanDescFactory.getBeanDesc(daoClass);\r
134         final Connection conn = getConnection();// It is first impact to Database!\r
135         try {\r
136             final DatabaseMetaData dbMetaData = getMetaData(conn);\r
137             dbms = DbmsManager.getDbms(getDatabaseProductName(dbMetaData));\r
138         } finally {\r
139             close(conn);\r
140         }\r
141         this.beanMetaData = beanMetaDataFactory.createBeanMetaData(daoInterface, beanClass);\r
142         checkSingleRowUpdateForAll = daoAnnotationReader.isCheckSingleRowUpdate();\r
143 \r
144         // Comment out for lazy-load!\r
145         // setupSqlCommand();\r
146     }\r
147 \r
148     // ===================================================================================\r
149     //                                                           SqlCommand Setup Override\r
150     //                                                           =========================\r
151     @Override\r
152     public SqlCommand getSqlCommand(String methodName) throws MethodNotFoundRuntimeException {\r
153         SqlCommand cmd = (SqlCommand) sqlCommands.get(methodName);\r
154         if (cmd != null) {\r
155             return cmd;\r
156         }\r
157         synchronized (_methodInitializationLockMonitor) {\r
158             cmd = (SqlCommand) sqlCommands.get(methodName);\r
159             if (cmd != null) {\r
160                 if (_log.isDebugEnabled()) {\r
161                     _log.debug("...Getting sqlCommand as cache because the previous thread have already initilized.");\r
162                 }\r
163                 return cmd;\r
164             }\r
165             if (_log.isDebugEnabled()) {\r
166                 _log.debug("...Initializing sqlCommand for " + methodName + "().");\r
167             }\r
168             cmd = initializeSqlCommand(methodName);\r
169         }\r
170         return cmd;\r
171     }\r
172 \r
173     protected SqlCommand initializeSqlCommand(String methodName) throws MethodNotFoundRuntimeException {\r
174         if (OutsideSqlContext.isExistOutsideSqlContextOnThread()) {\r
175             final OutsideSqlContext outsideSqlContext = OutsideSqlContext.getOutsideSqlContextOnThread();\r
176             if (outsideSqlContext != null && outsideSqlContext.isSpecifiedOutsideSql()) {\r
177                 return initializeSpecifiedOutsideSqlCommand(methodName, outsideSqlContext);\r
178             }\r
179         }\r
180         final Method[] methods = daoBeanDesc.getMethods(methodName);\r
181         if (methods.length == 1 && MethodUtil.isAbstract(methods[0])) {\r
182             setupMethod(methods[0]);\r
183         }\r
184         final SqlCommand cmd = (SqlCommand) sqlCommands.get(methodName);\r
185         if (cmd != null) {\r
186             return cmd;\r
187         }\r
188         throw new MethodNotFoundRuntimeException(daoClass, methodName, null);\r
189     }\r
190 \r
191     protected SqlCommand initializeSpecifiedOutsideSqlCommand(String sqlCommandKey, OutsideSqlContext outsideSqlContext) throws MethodNotFoundRuntimeException {\r
192         final Method[] methods = daoBeanDesc.getMethods(outsideSqlContext.getMethodName());// By real method name.\r
193         if (methods.length == 1 && org.seasar.framework.util.MethodUtil.isAbstract(methods[0])) {\r
194             final Method method = methods[0];\r
195             if (isOutsideSqlDaoMethodSelect(method)) {\r
196                 setupSpecifiedOutsideSqlSelectCommand(sqlCommandKey, method, outsideSqlContext);\r
197             } else if (isOutsideSqlDaoMethodCall(method)) {\r
198                 setupSpecifiedOutsideSqlCallCommand(sqlCommandKey, method, outsideSqlContext);\r
199             } else {\r
200                 setupSpecifiedOutsideSqlExecuteCommand(sqlCommandKey, method, outsideSqlContext);\r
201             }\r
202         }\r
203         final SqlCommand cmd = (SqlCommand) sqlCommands.get(sqlCommandKey);\r
204         if (cmd != null) {\r
205             return cmd;\r
206         }\r
207         String msg = "Internal Error! The sql-command is not found:";\r
208         msg = msg + " sqlCommandKey=" + sqlCommandKey;\r
209         msg = msg + " sqlCommands=" + sqlCommands;\r
210         throw new IllegalStateException(msg);\r
211     }\r
212 \r
213     protected boolean isOutsideSqlDaoMethodSelect(Method method) {\r
214         return method.getName().startsWith("select");\r
215     }\r
216 \r
217     protected boolean isOutsideSqlDaoMethodCall(Method method) {\r
218         return method.getName().startsWith("call");\r
219     }\r
220 \r
221     // ===================================================================================\r
222     //                                                                     Assert Override\r
223     //                                                                     ===============\r
224     @Override\r
225     protected void setupMethodByAnnotation(Class daoInterface, Method method) {\r
226         final String sql = daoAnnotationReader.getSQL(method, dbms.getSuffix());\r
227         assertSQLAnnotationUnsupported(method, sql);\r
228         super.setupMethodByAnnotation(daoInterface, method);\r
229     }\r
230 \r
231     protected void assertSQLAnnotationUnsupported(final Method method, String sql) {\r
232         if (sql != null) {\r
233             throwS2DaoSQLAnnotationUnsupportedException(method, sql);\r
234         }\r
235     }\r
236 \r
237     protected void throwS2DaoSQLAnnotationUnsupportedException(final Method method, String sql) {\r
238         String msg = "Look! Read the message below." + getLineSeparator();\r
239         msg = msg + "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *" + getLineSeparator();\r
240         msg = msg + "Sorry, the SQL annotation of S2Dao is unsupported on DBFlute!" + getLineSeparator();\r
241         msg = msg + getLineSeparator();\r
242         msg = msg + "[Advice]" + getLineSeparator();\r
243         msg = msg + "Please use outside-sql of behavior." + getLineSeparator();\r
244         msg = msg + "  For example:" + getLineSeparator();\r
245         msg = msg + "    memberBhv.outsideSql().selectList(...)" + getLineSeparator();\r
246         msg = msg + getLineSeparator();\r
247         msg = msg + "If you've got to use it, you can set the property:" + getLineSeparator();\r
248         msg = msg + "{torque.isCompatibleS2DaoSQLAnnotationValid = true}" + getLineSeparator();\r
249         msg = msg + "But pay attention to version up of DBFlute" + getLineSeparator();\r
250         msg = msg + " because the property will not always supported at the future." + getLineSeparator();\r
251         msg = msg + getLineSeparator();\r
252         msg = msg + "[Method]" + getLineSeparator() + method + getLineSeparator();\r
253         msg = msg + getLineSeparator();\r
254         msg = msg + "[SQL]" + getLineSeparator() + sql + getLineSeparator();\r
255         msg = msg + "* * * * * * * * * */";\r
256         throw new UnsupportedOperationException(msg);\r
257     }\r
258 \r
259     @Override\r
260     protected void setupMethodByAuto(Method method) {\r
261         final OutsideSql outsideSql = method.getAnnotation(OutsideSql.class);\r
262         if (outsideSql != null) {\r
263             String msg = "This method '" + method.getName() + "()' should use Outside Sql but the file was not found!";\r
264             msg = msg + " Expected sql file name is '" + method.getDeclaringClass().getSimpleName() + "_" + method.getName() + ".sql'";\r
265             throw new IllegalStateException(msg);\r
266         }\r
267         super.setupMethodByAuto(method);\r
268     }\r
269 \r
270     // ===================================================================================\r
271     //                                                              ConditionBean Override\r
272     //                                                              ======================\r
273     @Override\r
274     protected void setupSelectMethodByAuto(final Method method) {\r
275         if (setupInternalSelectMethodSequenceNextVal(method)) { // For sequence\r
276             return;\r
277         }\r
278         if (setupInternalSelectMethodEntityByIdsForBuri(method)) { // For Buri\r
279             return;\r
280         }\r
281 \r
282         // Assert unsupported\r
283         final String query = daoAnnotationReader.getQuery(method);\r
284         assertQueryAnnotationUnsupported(method, query);\r
285         final String[] argNames = daoAnnotationReader.getArgNames(method);\r
286         assertAutoQueryByArgsAnnotationUnsupported(method, argNames);\r
287 \r
288         // Here it is the only method that the argument is DTO.\r
289         final ResultSetHandler handler = createResultSetHandler(method);\r
290         final SqlCommand cmd = setupInternalNonQuerySelectMethodByDto(method, handler);\r
291 \r
292         putSqlCommand(method.getName(), cmd);\r
293     }\r
294 \r
295     protected boolean setupInternalSelectMethodSequenceNextVal(final Method method) { // For sequence\r
296         if (!"selectNextVal".equals(method.getName())) {\r
297             return false;\r
298         }\r
299         final DBMeta dbmeta = findDBMeta();\r
300         if (!dbmeta.hasSequence()) {\r
301             String msg = "If the method 'selectNextVal()' exists, DBMeta.hasSequence() should return true:";\r
302             msg = msg + " dbmeta.hasSequence()=" + dbmeta.hasSequence() + " method=" + method;\r
303             throw new IllegalStateException(msg);\r
304         }\r
305         final String nextValSql = dbmeta.getSequenceNextValSql();\r
306         if (nextValSql == null) {\r
307             String msg = "If the method 'selectNextVal()' exists, DBMeta.getSequenceNextValSql() should not return null:";\r
308             msg = msg + " dbmeta.getSequenceNextValSql()=" + dbmeta.getSequenceNextValSql() + " method=" + method;\r
309             throw new IllegalStateException(msg);\r
310         }\r
311         setupSelectMethodByManual(method, nextValSql);\r
312         return true;\r
313     }\r
314 \r
315     protected boolean setupInternalSelectMethodEntityByIdsForBuri(final Method method) { // For Buri\r
316         if (!"getEntityByIds".equals(method.getName())) {\r
317             return false;\r
318         }\r
319         final ResultSetHandler handler = createResultSetHandler(method);\r
320         final String[] argNames = daoAnnotationReader.getArgNames(method);\r
321         final String query = daoAnnotationReader.getQuery(method);\r
322         if (query == null) {\r
323             String msg = "The method 'getEntityByIds()' should have QUERY annotation:";\r
324             msg = msg + " method=" + method;\r
325             throw new IllegalStateException(msg);\r
326         }\r
327         final Class[] types = method.getParameterTypes();\r
328         final SelectDynamicCommand cmd = createSelectDynamicCommand(handler, query);\r
329         cmd.setArgNames(argNames);\r
330         cmd.setArgTypes(types);\r
331         putSqlCommand(method.getName(), cmd);\r
332         return true;\r
333     }\r
334 \r
335     protected void assertQueryAnnotationUnsupported(final Method method, String query) {\r
336         if (query != null) {\r
337             String msg = "Sorry! The QUERY annotation of S2Dao is unsupported on DBFlute:";\r
338             msg = msg + " query=" + query + " method=" + method;\r
339             throw new UnsupportedOperationException(msg);\r
340         }\r
341     }\r
342 \r
343     protected void assertAutoQueryByArgsAnnotationUnsupported(final Method method, String[] argNames) {\r
344         if (!isAutoSelectSqlByDto(method, argNames)) {\r
345             String msg = "Sorry! The auto query by ARGS annotation of S2Dao is unsupported on DBFlute:";\r
346             msg = msg + " argNames=" + argNames + " method=" + method;\r
347             throw new UnsupportedOperationException(msg);\r
348         }\r
349     }\r
350 \r
351     // For condition-bean!\r
352     protected SqlCommand setupInternalNonQuerySelectMethodByDto(Method method, ResultSetHandler handler) {\r
353         final Class[] argTypes = method.getParameterTypes();\r
354         assertAutoQueryByDtoUnsupported(method, argTypes);\r
355         final S2DaoSelectDynamicCommand cmd = createCustomizeSelectDynamicCommand(handler);\r
356         cmd.setArgNames(new String[] { "dto" });\r
357         cmd.setArgTypes(argTypes);\r
358         return cmd;\r
359     }\r
360 \r
361     protected void assertAutoQueryByDtoUnsupported(final Method method, Class[] argTypes) {\r
362         final Class firstArgType = argTypes[0];\r
363         if (!ConditionBeanContext.isTheTypeConditionBean(firstArgType)) {\r
364             String msg = "Sorry! The auto query by DTO of S2Dao is unsupported on DBFlute:";\r
365             msg = msg + " dto=" + firstArgType + " method=" + method;\r
366             throw new UnsupportedOperationException(msg);\r
367         }\r
368     }\r
369 \r
370     // ===================================================================================\r
371     //                                       Insert and Update and Delete By Auto Override\r
372     //                                       =============================================\r
373     // -----------------------------------------------------\r
374     //                                                Insert\r
375     //                                                ------\r
376     @Override\r
377     protected void setupInsertMethodByAuto(final Method method) {\r
378         checkAutoUpdateMethod(method);\r
379         final String[] propertyNames = getPersistentPropertyNames(method);\r
380         final SqlCommand command;\r
381         if (isUpdateSignatureForBean(method)) {\r
382             final InternalInsertAutoDynamicCommand cmd = new InternalInsertAutoDynamicCommand();\r
383             cmd.setBeanMetaData(getBeanMetaData());\r
384             cmd.setDataSource(dataSource);\r
385             \r
386             // It is unnecessary for DBFlute!\r
387             // cmd.setNotSingleRowUpdatedExceptionClass(getNotSingleRowUpdatedExceptionClass(method));\r
388             \r
389             cmd.setPropertyNames(propertyNames);\r
390             cmd.setStatementFactory(statementFactory);\r
391             cmd.setCheckSingleRowUpdate(isCheckSingleRowUpdate(method));\r
392             command = cmd;\r
393         } else {\r
394             boolean returningRows = false;\r
395             if (int[].class.isAssignableFrom(method.getReturnType())) {\r
396                 returningRows = true;\r
397             }\r
398             final InternalInsertBatchAutoStaticCommand cmd = new InternalInsertBatchAutoStaticCommand(\r
399                     dataSource, statementFactory, getBeanMetaData(),\r
400                     propertyNames, returningRows);\r
401             command = cmd;\r
402         }\r
403         putSqlCommand(method.getName(), command);\r
404     }\r
405 \r
406     // -----------------------------------------------------\r
407     //                                                Update\r
408     //                                                ------\r
409     @Override\r
410     protected void setupUpdateMethodByAuto(final Method method) {\r
411         if (isFirstArgumentConditionBean(method)) {\r
412             final SqlCommand cmd = new InternalUpdateQueryAutoDynamicCommand(dataSource, statementFactory);\r
413             putSqlCommand(method.getName(), cmd);\r
414             return;\r
415         }\r
416         checkAutoUpdateMethod(method);\r
417         final String[] propertyNames = getPersistentPropertyNames(method);\r
418         SqlCommand cmd;\r
419         if (isUpdateSignatureForBean(method)) {\r
420             if (isUnlessNull(method.getName())) {\r
421                 cmd = createInternalUpdateAutoDynamicCommand(method, propertyNames);\r
422             } else if (isModifiedOnly(method.getName())) {\r
423                 cmd = createInternalUpdateModifiedOnlyCommand(method, propertyNames);\r
424             } else {\r
425                 cmd = createInternalUpdateAutoStaticCommand(method, propertyNames);\r
426             }\r
427         } else {\r
428             boolean returningRows = false;\r
429             if (int[].class.isAssignableFrom(method.getReturnType())) {\r
430                 returningRows = true;\r
431             }\r
432             cmd = createInternalUpdateBatchAutoStaticCommand(method, propertyNames, returningRows);\r
433         }\r
434         putSqlCommand(method.getName(), cmd);\r
435     }\r
436 \r
437     protected UpdateAutoStaticCommand createInternalUpdateAutoStaticCommand(final Method method, final String[] propertyNames) {\r
438         final UpdateAutoStaticCommand cmd = new UpdateAutoStaticCommand(dataSource, statementFactory, beanMetaData, propertyNames);\r
439         cmd.setCheckSingleRowUpdate(isCheckSingleRowUpdate(method));\r
440         return cmd;\r
441     }\r
442     \r
443     protected InternalUpdateAutoDynamicCommand createInternalUpdateAutoDynamicCommand(Method method, String[] propertyNames) {\r
444         final InternalUpdateAutoDynamicCommand cmd = newUpdateAutoDynamicCommand(method, dataSource, statementFactory);\r
445         cmd.setBeanMetaData(createBeanMetaData4UpdateDeleteByAuto(method));// Extension Point!\r
446         cmd.setPropertyNames(propertyNames);\r
447         cmd.setCheckSingleRowUpdate(!isNonstrictMethod(method));\r
448         \r
449         // It is unnecessary for DBFlute!\r
450         // cmd.setNotSingleRowUpdatedExceptionClass(getNotSingleRowUpdatedExceptionClass(method));\r
451         \r
452         cmd.setVersionNoAutoIncrementOnMemory(isUpdateVersionNoAutoIncrementOnMemory(method));\r
453         return cmd;\r
454     }\r
455 \r
456     protected InternalUpdateAutoDynamicCommand newUpdateAutoDynamicCommand(Method method, DataSource ds, StatementFactory sf) {\r
457         return new InternalUpdateAutoDynamicCommand(ds, sf);\r
458     }\r
459 \r
460     protected InternalUpdateModifiedOnlyCommand createInternalUpdateModifiedOnlyCommand(final Method method, final String[] propertyNames) {\r
461         final InternalUpdateModifiedOnlyCommand cmd = newInternalUpdateModifiedOnlyCommand(method, dataSource, statementFactory);\r
462         cmd.setBeanMetaData(createBeanMetaData4UpdateDeleteByAuto(method));// Extension Point!\r
463         cmd.setPropertyNames(propertyNames);\r
464         cmd.setCheckSingleRowUpdate(!isNonstrictMethod(method));\r
465         \r
466         // It is unnecessary for DBFlute!\r
467         // cmd.setNotSingleRowUpdatedExceptionClass(getNotSingleRowUpdatedExceptionClass(method));\r
468         \r
469         cmd.setVersionNoAutoIncrementOnMemory(isUpdateVersionNoAutoIncrementOnMemory(method));\r
470         return cmd;\r
471     }\r
472 \r
473     protected InternalUpdateModifiedOnlyCommand newInternalUpdateModifiedOnlyCommand(Method method, DataSource ds, StatementFactory sf) {\r
474         return new InternalUpdateModifiedOnlyCommand(ds, sf);\r
475     }\r
476 \r
477     protected InternalUpdateBatchAutoStaticCommand createInternalUpdateBatchAutoStaticCommand(final Method method, final String[] propertyNames, boolean returningRows) {\r
478         return new InternalUpdateBatchAutoStaticCommand(dataSource, statementFactory\r
479                                                       , createBeanMetaData4UpdateDeleteByAuto(method), propertyNames, returningRows\r
480                                                       , isUpdateVersionNoAutoIncrementOnMemory(method)) {\r
481             @Override\r
482             protected InternalUpdateBatchAutoHandler newInternalBatchAutoHandler() {\r
483                 return new InternalUpdateBatchAutoHandler(getDataSource(), getStatementFactory(), getBeanMetaData(), getPropertyTypes()) {\r
484                     @Override\r
485                     protected int[] executeBatch(PreparedStatement ps, List<?> list) {\r
486                         final int[] result = super.executeBatch(ps, list);\r
487                         try {\r
488                             handleBatchUpdateResultWithOptimisticLock(ps, list, result, method);\r
489                         } catch (SQLException e) {\r
490                             handleSQLException(e, ps, false);\r
491                             return null;// Unreachable!\r
492                         }\r
493                         return result;\r
494                     }\r
495                 };\r
496             }\r
497         };\r
498     }\r
499 \r
500     // -----------------------------------------------------\r
501     //                                                Delete\r
502     //                                                ------\r
503     @Override\r
504     protected void setupDeleteMethodByAuto(final Method method) {\r
505         if (isFirstArgumentConditionBean(method)) {\r
506             final SqlCommand cmd = new InternalDeleteQueryAutoDynamicCommand(dataSource, statementFactory);\r
507             putSqlCommand(method.getName(), cmd);\r
508             return;\r
509         }\r
510         checkAutoUpdateMethod(method);\r
511         final String[] propertyNames = getPersistentPropertyNames(method);\r
512         final SqlCommand cmd;\r
513         if (isUpdateSignatureForBean(method)) {\r
514             cmd = createInternalDeleteAutoStaticCommand(method, propertyNames);\r
515         } else {\r
516             boolean returningRows = false;\r
517             if (int[].class.isAssignableFrom(method.getReturnType())) {\r
518                 returningRows = true;\r
519             }\r
520             cmd = createInternalDeleteBatchAutoStaticCommand(method, propertyNames, returningRows);\r
521         }\r
522         putSqlCommand(method.getName(), cmd);\r
523     }\r
524 \r
525     protected InternalDeleteAutoStaticCommand createInternalDeleteAutoStaticCommand(final Method method, final String[] propertyNames) {\r
526         final InternalDeleteAutoStaticCommand cmd = new InternalDeleteAutoStaticCommand(dataSource, statementFactory, createBeanMetaData4UpdateDeleteByAuto(method), propertyNames);\r
527         cmd.setCheckSingleRowUpdate(!isNonstrictMethod(method));\r
528         return cmd;\r
529     }\r
530 \r
531     protected InternalDeleteBatchAutoStaticCommand createInternalDeleteBatchAutoStaticCommand(final Method method, final String[] propertyNames, boolean returningRows) {\r
532         return new InternalDeleteBatchAutoStaticCommand(dataSource, statementFactory, createBeanMetaData4UpdateDeleteByAuto(method), propertyNames, returningRows) {\r
533             @Override\r
534             protected InternalDeleteBatchAutoHandler newInternalBatchAutoHandler() {\r
535                 return new InternalDeleteBatchAutoHandler(getDataSource(), getStatementFactory(), getBeanMetaData(), getPropertyTypes()) {\r
536                     @Override\r
537                     protected int[] executeBatch(PreparedStatement ps, List<?> list) {\r
538                         final int[] result = super.executeBatch(ps, list);\r
539                         try {\r
540                             handleBatchUpdateResultWithOptimisticLock(ps, list, result, method);\r
541                         } catch (SQLException e) {\r
542                             handleSQLException(e, ps, false);\r
543                             return null;// Unreachable!\r
544                         }\r
545                         return result;\r
546                     }\r
547                 };\r
548             }\r
549         };\r
550     }\r
551 \r
552     // -----------------------------------------------------\r
553     //                                         Common Helper\r
554     //                                         -------------\r
555     protected BeanMetaData createBeanMetaData4UpdateDeleteByAuto(Method method) {\r
556         if (isNonstrictMethod(method)) {\r
557             return createNonConcurrencyBmdFactory().createBeanMetaData(getBeanClass());\r
558         } else {\r
559             return getBeanMetaData();\r
560         }\r
561     }\r
562     \r
563     protected boolean isUpdateVersionNoAutoIncrementOnMemory(Method method) {\r
564         return !isNonstrictMethod(method);\r
565     }\r
566 \r
567     protected boolean isNonstrictMethod(Method method) {\r
568         return method.getName().contains("Nonstrict");\r
569     }\r
570 \r
571     protected BeanMetaDataFactory createNonConcurrencyBmdFactory() {\r
572         final S2BeanMetaDataFactoryImpl nonConcurrencyBmdFactory = new S2BeanMetaDataFactoryImpl() {\r
573             protected BeanMetaDataImpl createBeanMetaDataImpl() {\r
574                 return new BeanMetaDataImpl() {\r
575                     public boolean hasVersionNoPropertyType() {\r
576                         return false;\r
577                     }\r
578 \r
579                     public boolean hasTimestampPropertyType() {\r
580                         return false;\r
581                     }\r
582                 };\r
583             }\r
584         };\r
585         nonConcurrencyBmdFactory.setAnnotationReaderFactory(this.annotationReaderFactory);\r
586         nonConcurrencyBmdFactory.setPropertyTypeFactoryBuilder(this.propertyTypeFactoryBuilder);\r
587         nonConcurrencyBmdFactory.setRelationPropertyTypeFactoryBuilder(this.relationPropertyTypeFactoryBuilder);\r
588         nonConcurrencyBmdFactory.setTableNaming(this.tableNaming);\r
589         nonConcurrencyBmdFactory.setDataSource(this.dataSource);\r
590         nonConcurrencyBmdFactory.setDaoNamingConvention(this.daoNamingConvention);\r
591         nonConcurrencyBmdFactory.setBeanEnhancer(this.beanEnhancer);\r
592         return nonConcurrencyBmdFactory;\r
593     }\r
594 \r
595     protected boolean isFirstArgumentConditionBean(final Method method) {\r
596         final Class<?>[] pmbTypes = method.getParameterTypes();\r
597         return pmbTypes.length > 0 && ConditionBean.class.isAssignableFrom(pmbTypes[0]);\r
598     }\r
599 \r
600     protected void handleBatchUpdateResultWithOptimisticLock(PreparedStatement ps, List<?> list, int[] result, Method method) throws SQLException {\r
601         if (ConditionBeanContext.isOracle()) {\r
602             final int updateCount = ps.getUpdateCount();\r
603             handleBatchUpdateResultWithOptimisticLockByUpdateCount(list, updateCount, method);\r
604         } else {\r
605             handleBatchUpdateResultWithOptimisticLockByResult(list, result, method);\r
606         }\r
607     }\r
608 \r
609     protected void handleBatchUpdateResultWithOptimisticLockByUpdateCount(List<?> list, int updateCount, Method method) {\r
610         if (list.isEmpty()) {\r
611             return;// for Safety!\r
612         }\r
613         if (updateCount < 0) {\r
614             return;// for Safety!\r
615         }\r
616         final int entityCount = list.size();\r
617         if (updateCount < entityCount) {\r
618             if (isNonstrictMethod(method)) {\r
619                 String msg = "The entity have already deleted:";\r
620                 msg = msg + " updateCount=" + updateCount;\r
621                 msg = msg + " entityCount=" + entityCount;\r
622                 msg = msg + " allEntities=" + list;\r
623                 throw new EntityAlreadyDeletedException(msg);\r
624             } else {\r
625                 throw new BatchEntityAlreadyUpdatedException(list.get(0), 0, updateCount);\r
626             }\r
627         }\r
628     }\r
629 \r
630     protected void handleBatchUpdateResultWithOptimisticLockByResult(List<?> list, Object result, Method method) {\r
631         if (list.isEmpty()) {\r
632             return;// for Safety!\r
633         }\r
634         if (!(result instanceof int[])) {\r
635             return;// for Safety!\r
636         }\r
637         final int[] updatedCountArray = (int[])result;\r
638         final int entityCount = list.size();\r
639         int index = 0;\r
640         boolean alreadyUpdated = false;\r
641         for (int oneUpdateCount : updatedCountArray) {\r
642             if (entityCount <= index) {\r
643                 break;// for Safety!\r
644             }\r
645             if (oneUpdateCount == 0) {\r
646                 alreadyUpdated = true;\r
647                 break;\r
648             } else if (oneUpdateCount > 1) {\r
649                 String msg = "The entity updated two or more records in batch update:";\r
650                 msg = msg + " entity=" + list.get(index);\r
651                 msg = msg + " updatedCount=" + oneUpdateCount;\r
652                 msg = msg + " allEntities=" + list;\r
653                 throw new EntityDuplicatedException(msg);\r
654             }\r
655             ++index;\r
656         }\r
657         if (alreadyUpdated) {\r
658             int updateCount = 0;\r
659             for (int oneUpdateCount : updatedCountArray) {\r
660                 updateCount = updateCount + oneUpdateCount;\r
661             }\r
662             if (isNonstrictMethod(method)) {\r
663                 String msg = "The entity have already deleted:";\r
664                 msg = msg + " entity=" + list.get(index);\r
665                 msg = msg + " updateCount=" + updateCount;\r
666                 msg = msg + " allEntities=" + list;\r
667                 throw new EntityAlreadyDeletedException(msg);\r
668             } else {\r
669                 throw new BatchEntityAlreadyUpdatedException(list.get(index), 0, updateCount);\r
670             }\r
671         }\r
672     }\r
673 \r
674     // ===================================================================================\r
675     //                                                                 OutsideSql Override\r
676     //                                                                 ===================\r
677     // -----------------------------------------------------\r
678     //                                     Normal OutsideSql\r
679     //                                     -----------------\r
680     @Override\r
681     protected void setupSelectMethodByManual(Method method, String sql) {\r
682         final Class<?>[] pmbTypes = method.getParameterTypes();\r
683         final String[] argNames = this.daoAnnotationReader.getArgNames(method);\r
684         final Class<?>[] argTypes;\r
685         if (pmbTypes != null && pmbTypes.length > 0\r
686                 && CursorHandler.class.isAssignableFrom(pmbTypes[pmbTypes.length-1])) {\r
687             argTypes = new Class<?>[pmbTypes.length - 1];\r
688             for (int i=0; i < pmbTypes.length - 1; i++) {\r
689                 argTypes[i] = pmbTypes[i];\r
690             }\r
691         } else {\r
692             argTypes = pmbTypes;\r
693         }\r
694         final BeanMetaData myBeanMetaData = getOutsideSqlBeanMetaData(method);\r
695         registerSqlCommand(method.getName(), method, sql, argNames, argTypes, myBeanMetaData);\r
696     }\r
697 \r
698     protected BeanMetaData getOutsideSqlBeanMetaData(Method method) {\r
699         final Class beanClass4SelectMethodByManual = getOutsideSqlDefaultBeanClass(method);\r
700         if (beanClass4SelectMethodByManual.equals(getBeanClass())) {\r
701             return getBeanMetaData();\r
702         }\r
703         return createOutsideSqlCustomizeBeanMetaDataFactory().createBeanMetaData(getOutsideSqlDefaultBeanClass(method));\r
704     }\r
705 \r
706     @Override\r
707     protected void setupUpdateMethodByManual(Method method, final String sql) {\r
708         // DBFlute Extesion does not exist. Because DBFlute methods don't use this!\r
709         // The insert/update/delete methods on DAO interface as outside SQL are target.\r
710         // And especially NonPrimaryInsertMethod uses this for using S2Dao's BindVariableNode.\r
711         super.setupUpdateMethodByManual(method, sql);\r
712     }\r
713 \r
714     // -----------------------------------------------------\r
715     //                                  Specified OutsideSql\r
716     //                                  --------------------\r
717     // - - - - - - - - - - - -\r
718     //                  Select\r
719     //                   - - -\r
720     protected void setupSpecifiedOutsideSqlSelectCommand(String sqlCommandKey, Method method, OutsideSqlContext outsideSqlContext) {\r
721         // - - - - - - - - - - - - - - - - - - - - - - -\r
722         // The attribute of Specified-OutsideSqlContext.\r
723         // - - - - - - - - - - - - - - - - - - - - - - -\r
724         final String sql = outsideSqlContext.readFilteredOutsideSql(getSqlFileEncoding(), dbms.getSuffix());\r
725         final Object pmb = outsideSqlContext.getParameterBean();\r
726         final Object resultTypeSpecification = outsideSqlContext.getResultTypeSpecification();\r
727 \r
728         // - - - - - - - - - - - - - - -\r
729         // The attribute of SqlCommand.\r
730         // - - - - - - - - - - - - - - -\r
731         final String[] argNames = (pmb != null ? new String[] {"pmb"} : new String[]{});\r
732         final Class<?>[] argTypes = (pmb != null ? new Class<?>[] {pmb.getClass()} : new Class<?>[]{});\r
733 \r
734         // - - - - - - - - - - - - - - - -\r
735         // Create customized BeanMetaData.\r
736         // - - - - - - - - - - - - - - - -\r
737         final Class<?> lastestArguementType = method.getParameterTypes()[method.getParameterTypes().length-1];\r
738         final ResultSetHandler myResultSetHandler;\r
739         if (Class.class.isAssignableFrom(lastestArguementType)) {\r
740             // - - - - - - - -\r
741             // EntityHandling\r
742             // - - - - - - - -\r
743             final Class<?> customizeEntityType = (Class<?>)resultTypeSpecification;\r
744             final BeanMetaData myBeanMetaData = createSpecifiedOutsideSqlCustomizeBeanMetaData(customizeEntityType);\r
745             if (List.class.isAssignableFrom(method.getReturnType())) {\r
746                 myResultSetHandler = createSpecifiedOutsideSqlCustomizeBeanListResultSetHandler(myBeanMetaData, customizeEntityType);\r
747             } else {\r
748                 throw new UnsupportedOperationException("The return type of method is unsupported: method.getReturnType()=" + method.getReturnType());\r
749                 // myResultSetHandler = createSpecifiedOutsideSqlCustomizeBeanResultSetHandler(myBeanMetaData, customizeEntityType);\r
750             }\r
751         } else if (CursorHandler.class.isAssignableFrom(lastestArguementType)) {\r
752             // - - - - - - - -\r
753             // CursorHandling\r
754             // - - - - - - - -\r
755             final BeanMetaData myBeanMetaData = createSpecifiedOutsideSqlCursorBeanMetaData(method);\r
756             myResultSetHandler = createSpecifiedOutsideSqlCursorResultSetHandler(myBeanMetaData);\r
757         } else {\r
758             String msg = "The lastestArguementType is unsupported:";\r
759             msg = msg + " lastestArguementType=" + lastestArguementType;\r
760             msg = msg + " method=" + method;\r
761             throw new IllegalStateException(msg);\r
762         }\r
763 \r
764         // - - - - - - - - - - -\r
765         // Register Sql-Command.\r
766         // - - - - - - - - - - -\r
767         registerSqlCommand(sqlCommandKey, method, sql, argNames, argTypes, myResultSetHandler);\r
768     }\r
769 \r
770     protected BeanMetaData createSpecifiedOutsideSqlCustomizeBeanMetaData(Class clazz) {\r
771         return createOutsideSqlCustomizeBeanMetaDataFactory().createBeanMetaData(clazz);\r
772     }\r
773 \r
774     /**\r
775      * Create the handler of result set of specified outside-sql for the list of customize bean.\r
776      * @param specifiedBeanMetaData Specified bean meta data. (NotNull)\r
777      * @param customizeEntityType The type of customize entity. (NotNull)\r
778      * @return The handler of result set. (NotNull)\r
779      */\r
780     protected ResultSetHandler createSpecifiedOutsideSqlCustomizeBeanListResultSetHandler(BeanMetaData specifiedBeanMetaData, Class<?> customizeEntityType) {\r
781         final ValueType valueType = ValueTypes.getValueType(customizeEntityType);\r
782         if (valueType == null || !valueType.equals(ValueTypes.OBJECT)) {\r
783             return new InternalObjectListResultSetHandler(valueType);\r
784         }\r
785         final InternalRowCreator rowCreator = createSpecifiedOutsideSqlInternalRowCreator(specifiedBeanMetaData);\r
786         final InternalRelationRowCreator relationRowCreator = createSpecifiedOutsideSqlInternalRelationRowCreator(specifiedBeanMetaData);\r
787         return new InternalBeanListMetaDataResultSetHandler(specifiedBeanMetaData, rowCreator, relationRowCreator);\r
788     }\r
789 \r
790     protected InternalRowCreator createSpecifiedOutsideSqlInternalRowCreator(BeanMetaData bmd) {\r
791         final Class clazz = bmd.getBeanClass();\r
792         return InternalRowCreator.createInternalRowCreator(clazz);\r
793     }\r
794 \r
795     protected InternalRelationRowCreator createSpecifiedOutsideSqlInternalRelationRowCreator(BeanMetaData bmd) {\r
796         return new InternalRelationRowCreator();\r
797     }\r
798 \r
799     protected class InternalObjectListResultSetHandler implements ResultSetHandler {\r
800         private ValueType valueType;\r
801         public InternalObjectListResultSetHandler(ValueType valueType) {\r
802             this.valueType = valueType;\r
803         }\r
804         public Object handle(ResultSet rs) throws SQLException {\r
805             final List<Object> ret = new ArrayList<Object>();\r
806             while (rs.next()) {\r
807                 ret.add(valueType.getValue(rs, 1));\r
808             }\r
809             return ret;\r
810         }\r
811     }\r
812 \r
813     protected BeanMetaData createSpecifiedOutsideSqlCursorBeanMetaData(Method method) {\r
814         return createOutsideSqlCustomizeBeanMetaDataFactory().createBeanMetaData(getOutsideSqlDefaultBeanClass(method));\r
815     }\r
816 \r
817     protected ResultSetHandler createSpecifiedOutsideSqlCursorResultSetHandler(BeanMetaData specifiedBeanMetaData) {\r
818         return new org.seasar.extension.jdbc.impl.ObjectResultSetHandler();// This is dummy for cursor handling!\r
819     }\r
820 \r
821     // - - - - - - - - - - - -\r
822     //                 Execute\r
823     //                 - - - -\r
824     protected void setupSpecifiedOutsideSqlExecuteCommand(String sqlCommandKey, Method method, OutsideSqlContext outsideSqlContext) {\r
825         // - - - - - - - - - - - - - - - - - - - - - - -\r
826         // The attribute of Specified-OutsideSqlContext.\r
827         // - - - - - - - - - - - - - - - - - - - - - - -\r
828         final String sql = outsideSqlContext.readFilteredOutsideSql(getSqlFileEncoding(), dbms.getSuffix());\r
829         final Object pmb = outsideSqlContext.getParameterBean();\r
830 \r
831         // - - - - - - - - - - - - - - -\r
832         // The attribute of SqlCommand.\r
833         // - - - - - - - - - - - - - - -\r
834         final String[] argNames = (pmb != null ? new String[] {"pmb"} : new String[]{});\r
835         final Class<?>[] argTypes = (pmb != null ? new Class<?>[] {pmb.getClass()} : new Class<?>[]{});\r
836 \r
837         final InternalUpdateDynamicCommand cmd = new InternalUpdateDynamicCommand(dataSource, statementFactory) {\r
838             @Override\r
839             public Object execute(Object[] args) {\r
840                 if (args.length != 3) {\r
841                     String msg = "Internal Error! OutsideSqlDao.execute() should have 3 arguements: args.length=" + args.length;\r
842                     throw new IllegalStateException(msg);\r
843                 }\r
844                 Object arg = args[1];\r
845                 return super.execute(new Object[] {arg});\r
846             }\r
847         };\r
848         \r
849         // It is unnecessary for DBFlute!\r
850         // cmd.setNotSingleRowUpdatedExceptionClass(getNotSingleRowUpdatedExceptionClass(method));\r
851         \r
852         registerSqlCommand(sqlCommandKey, method, sql, argNames, argTypes, cmd);\r
853     }\r
854 \r
855         // - - - - - - - - - - - -\r
856     //          Call Procedure\r
857     //           - - - - - - -\r
858     protected void setupSpecifiedOutsideSqlCallCommand(String sqlCommandKey, Method method, OutsideSqlContext outsideSqlContext) {\r
859         // - - - - - - - - - - - - - - - - - - - - - - -\r
860         // The attribute of Specified-OutsideSqlContext.\r
861         // - - - - - - - - - - - - - - - - - - - - - - -\r
862         final Object pmb = outsideSqlContext.getParameterBean();\r
863         final String procedureName = outsideSqlContext.getOutsideSqlPath();\r
864 \r
865         // - - - - - - - - - - - - - - -\r
866         // The attribute of SqlCommand.\r
867         // - - - - - - - - - - - - - - -\r
868         final InternalProcedureMetaDataFactory factory = createInternalProcedureMetaDataFactory();\r
869         factory.setValueTypeFactory(valueTypeFactory);\r
870         final Class<?> pmbType = pmb != null ? pmb.getClass() : null;\r
871         final InternalProcedureMetaData metaData = factory.createProcedureMetaData(procedureName, pmbType);\r
872         final InternalProcedureCommand cmd = createInternalProcedureCommand(method, metaData);\r
873         putSqlCommand(sqlCommandKey, cmd);\r
874     }\r
875 \r
876     protected InternalProcedureMetaDataFactory createInternalProcedureMetaDataFactory() {\r
877         return new InternalProcedureMetaDataFactory();\r
878     }\r
879 \r
880     protected InternalProcedureCommand createInternalProcedureCommand(Method method, InternalProcedureMetaData metaData) {\r
881         final ResultSetHandler resultSetHandler = createResultSetHandler(method);\r
882         return new InternalProcedureCommand(dataSource, resultSetHandler, statementFactory, metaData);\r
883     }\r
884         \r
885     // -----------------------------------------------------\r
886     //                                  Common of OutsideSql\r
887     //                                  --------------------\r
888     protected BeanMetaDataFactory createOutsideSqlCustomizeBeanMetaDataFactory() {\r
889         final S2BeanMetaDataFactoryImpl originalBmdFactory = new S2BeanMetaDataFactoryImpl() {\r
890             protected BeanMetaDataImpl createBeanMetaDataImpl() {\r
891                 return newOutsideSqlCustomizeBeanMetaDataImpl();\r
892             }\r
893         };\r
894         originalBmdFactory.setAnnotationReaderFactory(this.annotationReaderFactory);\r
895         originalBmdFactory.setPropertyTypeFactoryBuilder(createOutsideSqlPropertyTypeFactoryBuilder());\r
896         originalBmdFactory.setRelationPropertyTypeFactoryBuilder(this.relationPropertyTypeFactoryBuilder);\r
897         originalBmdFactory.setTableNaming(this.tableNaming);\r
898         originalBmdFactory.setDataSource(this.dataSource);\r
899         originalBmdFactory.setDaoNamingConvention(this.daoNamingConvention);\r
900         originalBmdFactory.setBeanEnhancer(this.beanEnhancer);\r
901         return originalBmdFactory;\r
902     }\r
903 \r
904     protected BeanMetaDataImpl newOutsideSqlCustomizeBeanMetaDataImpl() {\r
905         return new OutsideSqlCustomizeBeanMetaDataImpl();\r
906     }\r
907 \r
908     protected static class OutsideSqlCustomizeBeanMetaDataImpl extends BeanMetaDataImpl {\r
909         // Though nothing to override, it uses original class just in case.\r
910     }\r
911 \r
912     protected S2DaoPropertyTypeFactoryBuilderExtension createOutsideSqlPropertyTypeFactoryBuilder() {\r
913         final S2DaoPropertyTypeFactoryBuilderExtension impl = new S2DaoPropertyTypeFactoryBuilderExtension();\r
914         if (columnNaming == null) {\r
915             String msg = "Internal Error! The columnNaming should not be null! {Failed to Injection!}";\r
916             throw new IllegalStateException(msg);\r
917         }\r
918         impl.setColumnNaming(columnNaming);\r
919         impl.setValueTypeFactory(valueTypeFactory);\r
920         return impl;\r
921     }\r
922 \r
923     protected Class getOutsideSqlDefaultBeanClass(Method method) {\r
924         final Class retType = method.getReturnType();\r
925         if (java.util.List.class.isAssignableFrom(retType)) {\r
926             final Class elementType = InternalMethodUtil.getElementTypeOfListFromReturnMethod(method);\r
927             if (elementType != null) {\r
928                 return elementType;\r
929             } else {\r
930                 return getBeanClass();\r
931             }\r
932         } else if (retType.isArray()) {\r
933             return retType.getComponentType();\r
934         } else if (retType.isPrimitive() || !ValueTypes.getValueType(retType).equals(ValueTypes.OBJECT)) {\r
935             return getBeanClass();\r
936         } else {\r
937             return retType;\r
938         }\r
939     }\r
940     \r
941     protected void registerSqlCommand(String sqlCommandKey, Method method, String sql, String[] argNames, Class[] argTypes, BeanMetaData myBeanMetaData) {\r
942         registerSqlCommand(sqlCommandKey, method, sql, argNames, argTypes, createResultSetHandler(myBeanMetaData, method));\r
943     }\r
944 \r
945     protected void registerSqlCommand(String sqlCommandKey, Method method, String sql, String[] argNames, Class[] argTypes, ResultSetHandler myResultSetHandler) {\r
946         final S2DaoSelectDynamicCommand cmd = createCustomizeSelectDynamicCommand(myResultSetHandler);\r
947         registerSqlCommand(sqlCommandKey, method, sql, argNames, argTypes, cmd);\r
948     }\r
949 \r
950     protected void registerSqlCommand(String sqlCommandKey, Method method, String sql, String[] argNames, Class[] argTypes, S2DaoSelectDynamicCommand cmd) {\r
951         cmd.setSql(sql);\r
952         cmd.setArgNames(argNames);\r
953         cmd.setArgTypes(argTypes);\r
954         this.sqlCommands.put(sqlCommandKey, cmd);\r
955     }\r
956 \r
957     protected void registerSqlCommand(String sqlCommandKey, Method method, String sql, String[] argNames, Class[] argTypes, InternalUpdateDynamicCommand cmd) {\r
958         cmd.setSql(sql);\r
959         cmd.setArgNames(argNames);\r
960         cmd.setArgTypes(argTypes);\r
961         this.sqlCommands.put(sqlCommandKey, cmd);\r
962     }\r
963 \r
964     // ===================================================================================\r
965     //                                                                     Common Handlnig\r
966     //                                                                     ===============\r
967     @Override\r
968     protected void putSqlCommand(String methodName, SqlCommand cmd) {\r
969         sqlCommands.put(methodName, cmd);\r
970     }\r
971     \r
972     protected boolean isCheckSingleRowUpdate(Method method) {\r
973         return checkSingleRowUpdateForAll & daoAnnotationReader.isCheckSingleRowUpdate(method);\r
974     }\r
975 \r
976     // ===================================================================================\r
977     //                                             Customize SelectDynamicCommand Creation\r
978     //                                             =======================================\r
979     /**\r
980      * Create the customize select dynamic command that is for all select SQL on DBFlute.\r
981      * @param handler The handler of result set. (NotNull)\r
982      * @return The customize select dynamic command. (NotNull)\r
983      */\r
984     protected S2DaoSelectDynamicCommand createCustomizeSelectDynamicCommand(ResultSetHandler handler) {\r
985         return new S2DaoSelectDynamicCommand(dataSource, statementFactory, handler);\r
986     }\r
987 \r
988     // ===================================================================================\r
989     //                                                           ResultSetHandler Override\r
990     //                                                           =========================\r
991     @Override\r
992     protected ResultSetHandler createResultSetHandler(Method method) {\r
993         return this.resultSetHandlerFactory.getResultSetHandler(daoAnnotationReader, beanMetaData, method);\r
994     }\r
995 \r
996     protected ResultSetHandler createResultSetHandler(BeanMetaData specifiedBeanMetaData, Method method) {// For specified BeanMetaData\r
997         return this.resultSetHandlerFactory.getResultSetHandler(daoAnnotationReader, specifiedBeanMetaData, method);\r
998     }\r
999 \r
1000     // ===================================================================================\r
1001     //                                                                      JDBC Delegator\r
1002     //                                                                      ==============\r
1003     protected Connection getConnection() {\r
1004         if (dataSource == null) {\r
1005             throw new IllegalStateException("The dataSource should not be null!");\r
1006         }\r
1007         try {\r
1008             return dataSource.getConnection();\r
1009         } catch (SQLException e) {\r
1010             handleSQLException(e, null);\r
1011             return null;// Unreachable!\r
1012         }\r
1013     }\r
1014 \r
1015     protected DatabaseMetaData getMetaData(Connection conn) {\r
1016         try {\r
1017             return conn.getMetaData();\r
1018         } catch (SQLException e) {\r
1019             handleSQLException(e, null);\r
1020             return null;// Unreachable!\r
1021         }\r
1022     }\r
1023 \r
1024     protected String getDatabaseProductName(DatabaseMetaData dbMetaData) {\r
1025         try {\r
1026             return dbMetaData.getDatabaseProductName();\r
1027         } catch (SQLException e) {\r
1028             handleSQLException(e, null);\r
1029             return null;// Unreachable!\r
1030         }\r
1031     }\r
1032 \r
1033     protected void close(Connection conn) {\r
1034         if (conn == null) {\r
1035             return;\r
1036         }\r
1037         try {\r
1038             conn.close();\r
1039         } catch (SQLException e) {\r
1040             handleSQLException(e, null);\r
1041         }\r
1042     }\r
1043 \r
1044     // ===================================================================================\r
1045     //                                                                  Exception Handlnig\r
1046     //                                                                  ==================\r
1047     protected void handleSQLException(SQLException e, Statement statement) {\r
1048         new SQLExceptionHandler().handleSQLException(e, statement);\r
1049     }\r
1050 \r
1051     // ===================================================================================\r
1052     //                                               ResultSetHandlerFactoryImpl Extension\r
1053     //                                               =====================================\r
1054     public static class ResultSetHandlerFactoryExtension extends ResultSetHandlerFactoryImpl {\r
1055         public ResultSetHandlerFactoryExtension() {\r
1056             super();\r
1057         }\r
1058 \r
1059         @Override\r
1060         protected RowCreator createRowCreator() { // [DAO-118] (2007/08/25)\r
1061             return createInternalRowCreator(null);\r
1062         }\r
1063 \r
1064         @Override\r
1065         protected RelationRowCreator createRelationRowCreator() {\r
1066             return createInternalRelationRowCreator(null);\r
1067         }\r
1068         \r
1069         @Override\r
1070         protected ResultSetHandler createBeanListMetaDataResultSetHandler(BeanMetaData bmd) { // DBFlute Target\r
1071             final InternalRowCreator rowCreator = createInternalRowCreator(bmd);\r
1072             final InternalRelationRowCreator relationRowCreator = createInternalRelationRowCreator(bmd);\r
1073             return new InternalBeanListMetaDataResultSetHandler(bmd, rowCreator, relationRowCreator);\r
1074         }\r
1075         \r
1076         @Override\r
1077         protected ResultSetHandler createBeanArrayMetaDataResultSetHandler(BeanMetaData bmd) { // DBFlute Target\r
1078             final InternalRowCreator rowCreator = createInternalRowCreator(bmd);\r
1079             final InternalRelationRowCreator relationRowCreator = createInternalRelationRowCreator(bmd);\r
1080             return new InternalBeanArrayMetaDataResultSetHandler(bmd, rowCreator, relationRowCreator);\r
1081         }\r
1082 \r
1083         protected InternalRowCreator createInternalRowCreator(BeanMetaData bmd) {\r
1084             final Class<?> clazz = bmd != null ? bmd.getBeanClass() : null;\r
1085             return InternalRowCreator.createInternalRowCreator(clazz);\r
1086         }\r
1087 \r
1088         protected InternalRelationRowCreator createInternalRelationRowCreator(BeanMetaData bmd) {\r
1089             return new InternalRelationRowCreator(); // Not yet implemented about performance tuning!\r
1090         }\r
1091     }\r
1092 \r
1093     // ===================================================================================\r
1094     //                                                                       Vert Internal\r
1095     //                                                                       =============\r
1096     protected static class InternalMethodUtil {\r
1097         public static Class getElementTypeOfListFromReturnMethod(Method method) {\r
1098             return InternalReflectionUtil.getElementTypeOfListFromReturnType(method);\r
1099         }\r
1100     }\r
1101     protected static class InternalReflectionUtil {\r
1102         public static Class<?> getElementTypeOfList(final Type parameterizedList) {\r
1103             if (!(parameterizedList instanceof ParameterizedType)) {\r
1104                 return null;\r
1105             }\r
1106             final ParameterizedType parameterizedType = ParameterizedType.class.cast(parameterizedList);\r
1107             final Type rawType = parameterizedType.getRawType();\r
1108             if (!(rawType instanceof Class)) {\r
1109                 return null;\r
1110             }\r
1111             final Class<?> rawClass = Class.class.cast(rawType);\r
1112             if (!rawClass.isAssignableFrom(List.class)) {\r
1113                 return null;\r
1114             }\r
1115             final Type[] actualTypeArgument = parameterizedType.getActualTypeArguments();\r
1116             if (actualTypeArgument == null || actualTypeArgument.length != 1) {\r
1117                 return null;\r
1118             }\r
1119             if (!(actualTypeArgument[0] instanceof Class)) {\r
1120                 return null;\r
1121             }\r
1122             return Class.class.cast(actualTypeArgument[0]);\r
1123         }\r
1124         public static Class<?> getElementTypeOfListFromParameterType(final Method method, final int parameterPosition) {\r
1125             final Type[] pmbTypes = method.getGenericParameterTypes();\r
1126             return getElementTypeOfList(pmbTypes[parameterPosition]);\r
1127         }\r
1128         public static Class<?> getElementTypeOfListFromReturnType(final Method method) {\r
1129             return getElementTypeOfList(method.getGenericReturnType());\r
1130         }\r
1131     }\r
1132 \r
1133     // ===================================================================================\r
1134     //                                                                       Assist Helper\r
1135     //                                                                       =============\r
1136     protected DBMeta findDBMeta() {\r
1137         final Class<?> beanType = getBeanClass();\r
1138         if (beanType == null) {\r
1139             return null;\r
1140         }\r
1141         if (!Entity.class.isAssignableFrom(beanType)) {\r
1142             return null;\r
1143         }\r
1144         final Entity entity;\r
1145         try {\r
1146             entity = (Entity)beanType.newInstance();\r
1147         } catch (InstantiationException e) {\r
1148             throw new IllegalStateException(e);\r
1149         } catch (IllegalAccessException e) {\r
1150             throw new IllegalStateException(e);\r
1151         }\r
1152         return entity.getDBMeta();\r
1153     }\r
1154 \r
1155     // ===================================================================================\r
1156     //                                                                      General Helper\r
1157     //                                                                      ==============\r
1158     protected String getLineSeparator() {\r
1159         return SimpleSystemUtil.getLineSeparator();\r
1160     }\r
1161 \r
1162     // ===================================================================================\r
1163     //                                                                            Accessor\r
1164     //                                                                            ========\r
1165     // -----------------------------------------------------\r
1166     //                                     Sql File Encoding\r
1167     //                                     -----------------\r
1168     public String getSqlFileEncoding() {\r
1169         return sqlFileEncoding;\r
1170     }\r
1171 \r
1172     // -----------------------------------------------------\r
1173     //                                         Bean Enhancer\r
1174     //                                         -------------\r
1175     public BeanEnhancer getBeanEnhancer() {\r
1176         return beanEnhancer;\r
1177     }\r
1178 \r
1179     public void setBeanEnhancer(final BeanEnhancer beanEnhancer) {\r
1180         this.beanEnhancer = beanEnhancer;\r
1181     }\r
1182 \r
1183     // -----------------------------------------------------\r
1184     //                             Annotation Reader Factory\r
1185     //                             -------------------------\r
1186     public void setAnnotationReaderFactory(org.seasar.dao.AnnotationReaderFactory annotationReaderFactory) {\r
1187         this.annotationReaderFactory = annotationReaderFactory;\r
1188     }\r
1189 \r
1190     // -----------------------------------------------------\r
1191     //                                  Version After 1.0.47\r
1192     //                                  --------------------\r
1193     public ColumnNaming getColumnNaming() {\r
1194         return columnNaming;\r
1195     }\r
1196 \r
1197     public void setColumnNaming(final ColumnNaming columnNaming) {\r
1198         this.columnNaming = columnNaming;\r
1199     }\r
1200 \r
1201     public PropertyTypeFactoryBuilder getPropertyTypeFactoryBuilder() {\r
1202         return propertyTypeFactoryBuilder;\r
1203     }\r
1204 \r
1205     public void setPropertyTypeFactoryBuilder(final PropertyTypeFactoryBuilder propertyTypeFactoryBuilder) {\r
1206         this.propertyTypeFactoryBuilder = propertyTypeFactoryBuilder;\r
1207     }\r
1208 \r
1209     public RelationPropertyTypeFactoryBuilder getRelationPropertyTypeFactoryBuilder() {\r
1210         return relationPropertyTypeFactoryBuilder;\r
1211     }\r
1212 \r
1213     public void setRelationPropertyTypeFactoryBuilder(final RelationPropertyTypeFactoryBuilder relationPropertyTypeFactoryBuilder) {\r
1214         this.relationPropertyTypeFactoryBuilder = relationPropertyTypeFactoryBuilder;\r
1215     }\r
1216 \r
1217     public TableNaming getTableNaming() {\r
1218         return tableNaming;\r
1219     }\r
1220 \r
1221     public void setTableNaming(final TableNaming tableNaming) {\r
1222         this.tableNaming = tableNaming;\r
1223     }\r
1224 \r
1225     // -----------------------------------------------------\r
1226     //                                     DBFlute Extension\r
1227     //                                     -----------------\r
1228     public BehaviorSelector getBehaviorSelector() {\r
1229         return _behaviorSelector;\r
1230     }\r
1231 \r
1232     public void setBehaviorSelector(final BehaviorSelector behaviorSelector) {\r
1233         this._behaviorSelector = behaviorSelector;\r
1234     }\r
1235     \r
1236     public boolean isInternalDebug() {\r
1237         return _internalDebug;\r
1238     }\r
1239 \r
1240     public void setInternalDebug(final boolean internalDebug) {\r
1241         this._internalDebug = internalDebug;\r
1242     }\r
1243 }\r