前回の記事で、WASからlookup()
して取得したDataSource
オブジェクト、およびDataSource
からgetConnection()
して取得したConnection
オブジェクトが、WASのクラスだったということまでわかりました。前回不完全だったプロパティの値をきちんと取得できるようにすることと、JDBCドライバ固有のプロパティ値をどこにどのように持っているかを確認します。
クラス情報取得
PropertyDescriptor
のgetValue()
メソッドではプロパティ値を取得できないことがわかったので、getter
をinvoke()
して値を取得する方法に変更します。この場合、そもそもの値がnull
だとNPEになるので、value=UNDEFINEDにしてスローした例外のgetMessage()
を添えることにしました(Method
は"not null"だが、invoke()
が返すObject
がnull
。つまりそもそも初期化されていない(null
)フィールド変数の場合、null.toString()
となりNPE)。[本当ならObject obj = m.invoke();
からif(obj != null)
などとした方がよいかも]
package net.mognet.servlet;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
public class ClassInfo {
protected static final String NOT_AVAILABLE = "N/A";
protected static final String UNKNOWN = "unkown";
protected static final String UNDEFINED = "undefined";
public static LinkedHashMap<String, String[]> getLinkedHashMap(Object obj) {
LinkedHashMap<String, String[]> map = new LinkedHashMap<String, String[]>();
BeanInfo bi = null;
PropertyDescriptor[] pd = null;
String name, type, value, getter, setter;
Method m;
try {
bi = Introspector.getBeanInfo(obj.getClass());
} catch (IntrospectionException e) {
e.printStackTrace();
}
pd = bi.getPropertyDescriptors();
for (int i = 0; i < pd.length; i++) {
name = pd[i].getName();
type = pd[i].getPropertyType().getName();
m = pd[i].getReadMethod();
if (m != null) {
getter = m.getName();
try {
value= m.invoke(obj).toString();
} catch (Exception e) {
System.err.println("Exception occured while processing " + m.getName() + " --- " + e.getMessage());
e.printStackTrace();
value = UNDEFINED + " (getter throwed " + e.getMessage() + " )";
}
} else {
getter = NOT_AVAILABLE;
value = UNKNOWN + " (getter method is not available)";
}
m = pd[i].getWriteMethod();
if (m != null) {
setter = m.getName();
} else {
setter = NOT_AVAILABLE;
}
map.put(name, new String[] { type, getter, setter, value });
}
return map;
}
public static String exposeAsMarkdown(Object obj) {
LinkedHashMap<String, String[]> map = getLinkedHashMap(obj);
final String SEPARATOR = "|";
final String HEADER = SEPARATOR + "name" + SEPARATOR + "type" + SEPARATOR + "getter" + SEPARATOR + "setter" + SEPARATOR
+ "value" + SEPARATOR + "\n";
String[] pi;
StringBuilder sb = new StringBuilder(HEADER);
sb.append("|:--|:--|:--|:--|:--|\n");
for (String name : map.keySet()) {
pi = map.get(name);
sb.append(SEPARATOR + name + SEPARATOR + pi[0] + SEPARATOR + pi[1] + SEPARATOR + pi[2]
+ SEPARATOR + pi[3] + SEPARATOR + "\n");
}
return sb.toString();
}
public static String exposeAsCSV(Object obj) {
LinkedHashMap<String, String[]> map = getLinkedHashMap(obj);
final String SEPARATOR = ",";
final String HEADER = "name" + SEPARATOR + "type" + SEPARATOR + "getter" + SEPARATOR + "setter" + SEPARATOR
+ "value\n";
String[] pi;
StringBuilder sb = new StringBuilder(HEADER);
for (String name : map.keySet()) {
pi = map.get(name);
sb.append(name + SEPARATOR + pi[0] + SEPARATOR + pi[1] + SEPARATOR + pi[2]
+ SEPARATOR + pi[3] + "\n");
}
return sb.toString();
}
public static String exposeAsHtmlTable(Object obj) {
LinkedHashMap<String, String[]> map = getLinkedHashMap(obj);
final String SEPARATOR = "</td><td>";
final String HEADER = "<th>name</th><th>type</th><th>getter</th><th>setter</th><th>value</th>\n";
String[] pi;
StringBuilder sb = new StringBuilder(HEADER);
for (String name : map.keySet()) {
pi = map.get(name);
sb.append("<tr><td>" + name + SEPARATOR + pi[0] + SEPARATOR + pi[1] + SEPARATOR
+ pi[2] + SEPARATOR + pi[3] + "</td></tr>\n");
}
return sb.toString();
}
}
サーブレット側のコードは変えていません。
package net.mognet.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.HashMap;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
class JDBCType {
private String className, jndiName;
public JDBCType(String className, String jndiName) {
this.className = className;
this.jndiName = jndiName;
}
public String getClassName() {
return this.className;
}
public String getJndiName() {
return this.jndiName;
}
}
@WebServlet({ "/JDBCProperty", "/*" })
public class JDBCProperty extends HttpServlet {
private static final long serialVersionUID = 1L;
public JDBCProperty() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HashMap<String, JDBCType> map = new HashMap<String, JDBCType>();
map.put("MySQL", new JDBCType("com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource", "jdbc/mysqlmaster"));
map.put("Db2", new JDBCType("com.ibm.db2.jcc.DB2ConnectionPoolDataSource", "jdbc/db2"));
map.put("Oracle", new JDBCType("oracle.jdbc.pool.OracleConnectionPoolDataSource", "jdbc/oracle"));
map.put("SQLServer", new JDBCType("com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource", "jdbc/sqlserver"));
PrintWriter out = response.getWriter();
response.setContentType("text/html; charset=UTF-8");
out.println("<html><head><title>jdbc property</title></head><body>");
try {
Context ctx = new InitialContext();
for(String key : map.keySet()) {
DataSource ds = (DataSource) ctx.lookup(map.get(key).getJndiName());
String title = key + "(" + ds.getClass().getName() + ")";
out.println("<h1>" + title + "</h1>");
String prop = ClassInfo.exposeAsMarkdown(ds);
out.println("<pre>");
out.println("# " + title);
out.println(prop);
out.println("</pre>");
java.sql.Connection cn = ds.getConnection();
title = key + "(" + cn.getClass().getName() + ")";
out.println("<h1>" + title + "</h1>");
prop = ClassInfo.exposeAsMarkdown(cn);
out.println("<pre>");
out.println("# " + title + "</br>");
out.println(prop);
out.println("</pre>");
}
} catch (NamingException | SQLException | IllegalArgumentException e) {
out.println("<p>" + e.getMessage() + "</p>");
}
out.println("</body></html>");
}
}
結果
取得した結果です。
Db2(com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource)
name | type | getter | setter | value |
---|---|---|---|---|
XADataSource | boolean | isXADataSource | N/A | false |
active | boolean | isActive | N/A | undefined (getter throwed null ) |
class | java.lang.Class | getClass | N/A | class com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource |
connection | java.sql.Connection | getConnection | N/A | com.ibm.ws.rsadapter.jdbc.WSJccSQLJPDQConnection@d1075528 |
containerManagedConnection | java.sql.Connection | getContainerManagedConnection | N/A | com.ibm.ws.rsadapter.jdbc.WSJccSQLJPDQConnection@244bf097 |
dataStoreHelper | com.ibm.websphere.rsadapter.DataStoreHelper | getDataStoreHelper | N/A | com.ibm.websphere.rsadapter.DB2UniversalDataStoreHelper@58fc7511 |
heterogeneousPoolingConfigured | boolean | isHeterogeneousPoolingConfigured | N/A | false |
internalDataStoreHelper | com.ibm.ws.rsadapter.spi.InternalDataStoreHelper | getInternalDataStoreHelper | N/A | com.ibm.ws.rsadapter.spi.InternalDB2UniversalDataStoreHelper@ad729dd3 |
logWriter | java.io.PrintWriter | getLogWriter | setLogWriter | undefined (getter throwed null ) |
loginTimeout | int | getLoginTimeout | setLoginTimeout | 0 |
parentLogger | java.util.logging.Logger | getParentLogger | N/A | undefined (getter throwed null ) |
reference | javax.naming.Reference | getReference | setReference | undefined (getter throwed null ) |
Db2(com.ibm.ws.rsadapter.jdbc.WSJccSQLJPDQConnection)
name | type | getter | setter | value |
---|---|---|---|---|
CRI | javax.resource.spi.ConnectionRequestInfo | getCRI | N/A | undefined (getter throwed null ) |
DB2Object | java.lang.Object | getDB2Object | N/A | com.ibm.db2.jcc.am.jf@b25d50d1 |
JNDIName | java.lang.String | getJNDIName | N/A | jdbc/db2 |
SQLJLogWriter | com.ibm.db2.jcc.SQLJLogWriter | getSQLJLogWriter | N/A | com.ibm.db2.jcc.t4.g@480047e3 |
aborted | boolean | isAborted | setAborted | false |
autoCommit | boolean | getAutoCommit | setAutoCommit | true |
catalog | java.lang.String | getCatalog | setCatalog | undefined (getter throwed null ) |
class | java.lang.Class | getClass | N/A | class com.ibm.ws.rsadapter.jdbc.WSJccSQLJPDQConnection |
clientInfo | java.util.Properties | getClientInfo | setClientInfo | undefined (getter throwed null ) |
clientInformation | java.util.Properties | getClientInformation | setClientInformation | undefined (getter throwed null ) |
closed | boolean | isClosed | N/A | false |
connectionContext | sqlj.runtime.ref.DefaultContext | getConnectionContext | N/A | sqlj.runtime.ref.DefaultContext@ec369231 |
currentAutoCommit | boolean | N/A | setCurrentAutoCommit | unkown (getter method is not available) |
currentTransactionIsolation | int | N/A | setCurrentTransactionIsolation | unkown (getter method is not available) |
databaseName | java.lang.String | getDatabaseName | N/A | MYDB |
helper | com.ibm.websphere.rsadapter.DataStoreHelper | getHelper | N/A | com.ibm.websphere.rsadapter.DB2UniversalDataStoreHelper@58fc7511 |
holdability | int | getHoldability | setHoldability | 2 |
internalDataStoreHelper | com.ibm.ws.rsadapter.spi.InternalDataStoreHelper | getInternalDataStoreHelper | N/A | com.ibm.ws.rsadapter.spi.InternalDB2UniversalDataStoreHelper@ad729dd3 |
isMonitoring | boolean | N/A | setIsMonitoring | unkown (getter method is not available) |
j2EEName | com.ibm.websphere.csi.J2EEName | getJ2EEName | setJ2EEName | JDBCProp#JDBCProp.war#net.mognet.servlet.JDBCProperty |
jccDriver | boolean | isJccDriver | N/A | true |
metaData | java.sql.DatabaseMetaData | getMetaData | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcDatabaseMetaData@b8fc117d |
monitoring | boolean | isMonitoring | N/A | false |
networkTimeout | int | getNetworkTimeout | N/A | undefined (getter throwed null ) |
readOnly | boolean | isReadOnly | setReadOnly | false |
reserved | boolean | isReserved | N/A | false |
schema | java.lang.String | getSchema | setSchema | undefined (getter throwed null ) |
shareable | boolean | isShareable | N/A | true |
state | int | getState | N/A | 0 |
stateString | java.lang.String | getStateString | N/A | ACTIVE |
systemMonitor | com.ibm.websphere.rsadapter.WSSystemMonitor | getSystemMonitor | N/A | com.ibm.websphere.rsadapter.WSSystemMonitor@1a666605 |
tracer | com.ibm.ejs.ras.TraceComponent | getTracer | N/A | com.ibm.ejs.ras.TraceComponent@5bc08027 |
transactionIsolation | int | getTransactionIsolation | setTransactionIsolation | 4 |
typeMap | java.util.Map | getTypeMap | setTypeMap | {} |
warnings | java.sql.SQLWarning | getWarnings | N/A | undefined (getter throwed null ) |
webSphereVersion | java.lang.String | getWebSphereVersion | N/A | 6.0 |
MySQL(com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource)
name | type | getter | setter | value |
---|---|---|---|---|
XADataSource | boolean | isXADataSource | N/A | false |
active | boolean | isActive | N/A | undefined (getter throwed null ) |
class | java.lang.Class | getClass | N/A | class com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource |
connection | java.sql.Connection | getConnection | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcConnection@643efc93 |
containerManagedConnection | java.sql.Connection | getContainerManagedConnection | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcConnection@54cdd8bb |
dataStoreHelper | com.ibm.websphere.rsadapter.DataStoreHelper | getDataStoreHelper | N/A | com.ibm.websphere.rsadapter.GenericDataStoreHelper@ebc0e4e8 |
heterogeneousPoolingConfigured | boolean | isHeterogeneousPoolingConfigured | N/A | false |
internalDataStoreHelper | com.ibm.ws.rsadapter.spi.InternalDataStoreHelper | getInternalDataStoreHelper | N/A | com.ibm.ws.rsadapter.spi.InternalGenericDataStoreHelper@5a0ab59e |
logWriter | java.io.PrintWriter | getLogWriter | setLogWriter | undefined (getter throwed null ) |
loginTimeout | int | getLoginTimeout | setLoginTimeout | 0 |
parentLogger | java.util.logging.Logger | getParentLogger | N/A | undefined (getter throwed null ) |
reference | javax.naming.Reference | getReference | setReference | undefined (getter throwed null ) |
MySQL(com.ibm.ws.rsadapter.jdbc.WSJdbcConnection)
name | type | getter | setter | value |
---|---|---|---|---|
CRI | javax.resource.spi.ConnectionRequestInfo | getCRI | N/A | undefined (getter throwed null ) |
JNDIName | java.lang.String | getJNDIName | N/A | jdbc/mysqlmaster |
aborted | boolean | isAborted | setAborted | false |
autoCommit | boolean | getAutoCommit | setAutoCommit | true |
catalog | java.lang.String | getCatalog | setCatalog | |
class | java.lang.Class | getClass | N/A | class com.ibm.ws.rsadapter.jdbc.WSJdbcConnection |
clientInfo | java.util.Properties | getClientInfo | setClientInfo | {} |
clientInformation | java.util.Properties | getClientInformation | setClientInformation | undefined (getter throwed null ) |
closed | boolean | isClosed | N/A | false |
helper | com.ibm.websphere.rsadapter.DataStoreHelper | getHelper | N/A | com.ibm.websphere.rsadapter.GenericDataStoreHelper@ebc0e4e8 |
holdability | int | getHoldability | setHoldability | 2 |
internalDataStoreHelper | com.ibm.ws.rsadapter.spi.InternalDataStoreHelper | getInternalDataStoreHelper | N/A | com.ibm.ws.rsadapter.spi.InternalGenericDataStoreHelper@5a0ab59e |
j2EEName | com.ibm.websphere.csi.J2EEName | getJ2EEName | setJ2EEName | JDBCProp#JDBCProp.war#net.mognet.servlet.JDBCProperty |
metaData | java.sql.DatabaseMetaData | getMetaData | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcDatabaseMetaData@5d4a2f00 |
networkTimeout | int | getNetworkTimeout | N/A | 0 |
readOnly | boolean | isReadOnly | setReadOnly | false |
reserved | boolean | isReserved | N/A | false |
schema | java.lang.String | getSchema | setSchema | undefined (getter throwed null ) |
shareable | boolean | isShareable | N/A | true |
state | int | getState | N/A | 0 |
stateString | java.lang.String | getStateString | N/A | ACTIVE |
systemMonitor | com.ibm.websphere.rsadapter.WSSystemMonitor | getSystemMonitor | N/A | undefined (getter throwed null ) |
transactionIsolation | int | getTransactionIsolation | setTransactionIsolation | 2 |
typeMap | java.util.Map | getTypeMap | setTypeMap | {} |
warnings | java.sql.SQLWarning | getWarnings | N/A | undefined (getter throwed null ) |
Oracle(com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource)
name | type | getter | setter | value |
---|---|---|---|---|
XADataSource | boolean | isXADataSource | N/A | false |
active | boolean | isActive | N/A | undefined (getter throwed null ) |
class | java.lang.Class | getClass | N/A | class com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource |
connection | java.sql.Connection | getConnection | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcConnection@29df4c18 |
containerManagedConnection | java.sql.Connection | getContainerManagedConnection | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcConnection@c7f34c7e |
dataStoreHelper | com.ibm.websphere.rsadapter.DataStoreHelper | getDataStoreHelper | N/A | com.ibm.websphere.rsadapter.Oracle11gDataStoreHelper@98c985fc |
heterogeneousPoolingConfigured | boolean | isHeterogeneousPoolingConfigured | N/A | false |
internalDataStoreHelper | com.ibm.ws.rsadapter.spi.InternalDataStoreHelper | getInternalDataStoreHelper | N/A | com.ibm.ws.rsadapter.spi.InternalOracle11gDataStoreHelper@cc38870a |
logWriter | java.io.PrintWriter | getLogWriter | setLogWriter | undefined (getter throwed null ) |
loginTimeout | int | getLoginTimeout | setLoginTimeout | 0 |
parentLogger | java.util.logging.Logger | getParentLogger | N/A | undefined (getter throwed null ) |
reference | javax.naming.Reference | getReference | setReference | undefined (getter throwed null ) |
Oracle(com.ibm.ws.rsadapter.jdbc.WSJdbcConnection)
name | type | getter | setter | value |
---|---|---|---|---|
CRI | javax.resource.spi.ConnectionRequestInfo | getCRI | N/A | undefined (getter throwed null ) |
JNDIName | java.lang.String | getJNDIName | N/A | jdbc/oracle |
aborted | boolean | isAborted | setAborted | false |
autoCommit | boolean | getAutoCommit | setAutoCommit | true |
catalog | java.lang.String | getCatalog | setCatalog | undefined (getter throwed null ) |
class | java.lang.Class | getClass | N/A | class com.ibm.ws.rsadapter.jdbc.WSJdbcConnection |
clientInfo | java.util.Properties | getClientInfo | setClientInfo | {} |
clientInformation | java.util.Properties | getClientInformation | setClientInformation | undefined (getter throwed null ) |
closed | boolean | isClosed | N/A | false |
helper | com.ibm.websphere.rsadapter.DataStoreHelper | getHelper | N/A | com.ibm.websphere.rsadapter.Oracle11gDataStoreHelper@98c985fc |
holdability | int | getHoldability | setHoldability | 1 |
internalDataStoreHelper | com.ibm.ws.rsadapter.spi.InternalDataStoreHelper | getInternalDataStoreHelper | N/A | com.ibm.ws.rsadapter.spi.InternalOracle11gDataStoreHelper@cc38870a |
j2EEName | com.ibm.websphere.csi.J2EEName | getJ2EEName | setJ2EEName | JDBCProp#JDBCProp.war#net.mognet.servlet.JDBCProperty |
metaData | java.sql.DatabaseMetaData | getMetaData | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcDatabaseMetaData@1e2424da |
networkTimeout | int | getNetworkTimeout | N/A | 0 |
readOnly | boolean | isReadOnly | setReadOnly | false |
reserved | boolean | isReserved | N/A | false |
schema | java.lang.String | getSchema | setSchema | ORAMASTER |
shareable | boolean | isShareable | N/A | true |
state | int | getState | N/A | 0 |
stateString | java.lang.String | getStateString | N/A | ACTIVE |
systemMonitor | com.ibm.websphere.rsadapter.WSSystemMonitor | getSystemMonitor | N/A | undefined (getter throwed null ) |
transactionIsolation | int | getTransactionIsolation | setTransactionIsolation | 2 |
typeMap | java.util.Map | getTypeMap | setTypeMap | {} |
warnings | java.sql.SQLWarning | getWarnings | N/A | undefined (getter throwed null ) |
SQLServer(com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource)
name | type | getter | setter | value |
---|---|---|---|---|
XADataSource | boolean | isXADataSource | N/A | false |
active | boolean | isActive | N/A | undefined (getter throwed null ) |
class | java.lang.Class | getClass | N/A | class com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource |
connection | java.sql.Connection | getConnection | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcConnection@59e24f6e |
containerManagedConnection | java.sql.Connection | getContainerManagedConnection | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcConnection@de49784d |
dataStoreHelper | com.ibm.websphere.rsadapter.DataStoreHelper | getDataStoreHelper | N/A | com.ibm.websphere.rsadapter.MicrosoftSQLServerDataStoreHelper@365d0d65 |
heterogeneousPoolingConfigured | boolean | isHeterogeneousPoolingConfigured | N/A | false |
internalDataStoreHelper | com.ibm.ws.rsadapter.spi.InternalDataStoreHelper | getInternalDataStoreHelper | N/A | com.ibm.ws.rsadapter.spi.InternalMicrosoftSQLServerDataStoreHelper@744356bd |
logWriter | java.io.PrintWriter | getLogWriter | setLogWriter | undefined (getter throwed null ) |
loginTimeout | int | getLoginTimeout | setLoginTimeout | 15 |
parentLogger | java.util.logging.Logger | getParentLogger | N/A | undefined (getter throwed null ) |
reference | javax.naming.Reference | getReference | setReference | undefined (getter throwed null ) |
SQLServer(com.ibm.ws.rsadapter.jdbc.WSJdbcConnection)
name | type | getter | setter | value |
---|---|---|---|---|
CRI | javax.resource.spi.ConnectionRequestInfo | getCRI | N/A | undefined (getter throwed null ) |
JNDIName | java.lang.String | getJNDIName | N/A | jdbc/sqlserver |
aborted | boolean | isAborted | setAborted | false |
autoCommit | boolean | getAutoCommit | setAutoCommit | true |
catalog | java.lang.String | getCatalog | setCatalog | master |
class | java.lang.Class | getClass | N/A | class com.ibm.ws.rsadapter.jdbc.WSJdbcConnection |
clientInfo | java.util.Properties | getClientInfo | setClientInfo | {} |
clientInformation | java.util.Properties | getClientInformation | setClientInformation | undefined (getter throwed null ) |
closed | boolean | isClosed | N/A | false |
helper | com.ibm.websphere.rsadapter.DataStoreHelper | getHelper | N/A | com.ibm.websphere.rsadapter.MicrosoftSQLServerDataStoreHelper@365d0d65 |
holdability | int | getHoldability | setHoldability | 1 |
internalDataStoreHelper | com.ibm.ws.rsadapter.spi.InternalDataStoreHelper | getInternalDataStoreHelper | N/A | com.ibm.ws.rsadapter.spi.InternalMicrosoftSQLServerDataStoreHelper@744356bd |
j2EEName | com.ibm.websphere.csi.J2EEName | getJ2EEName | setJ2EEName | JDBCProp#JDBCProp.war#net.mognet.servlet.JDBCProperty |
metaData | java.sql.DatabaseMetaData | getMetaData | N/A | com.ibm.ws.rsadapter.jdbc.WSJdbcDatabaseMetaData@f24763f8 |
networkTimeout | int | getNetworkTimeout | N/A | undefined (getter throwed null ) |
readOnly | boolean | isReadOnly | setReadOnly | false |
reserved | boolean | isReserved | N/A | false |
schema | java.lang.String | getSchema | setSchema | dbo |
shareable | boolean | isShareable | N/A | true |
state | int | getState | N/A | 0 |
stateString | java.lang.String | getStateString | N/A | ACTIVE |
systemMonitor | com.ibm.websphere.rsadapter.WSSystemMonitor | getSystemMonitor | N/A | undefined (getter throwed null ) |
transactionIsolation | int | getTransactionIsolation | setTransactionIsolation | 4 |
typeMap | java.util.Map | getTypeMap | setTypeMap | {} |
warnings | java.sql.SQLWarning | getWarnings | N/A | undefined (getter throwed null ) |
JDBCドライバ特有のプロパティはほとんど隠蔽されるようです(MySQL用DataSourceのカスタムプロパティにいくつか値をいれてみましたが、Connection
のclientInfo
の値変わらず)。これらを眺めているとどんな管理をしているのかがなんとなくみえてきたりしますね。autoCommit
をOFFにしたりreadOnly
をONにしてみたりすることで、どんな動きをするのかとか。
トランザクション分離レベル
特に、トランザクション分離レベル(transactionIsolation)はDBエンジン特有の初期値になっています。
|DB|レベル|java.sql.connectionの定数1|ロストアップデート|ダーティーリード|ノンリピータブルリード|ファントムリード|
|:--|:--|:--|:--|:--|:--|:--|:--|
|Db2|4|TRANSACTION_REPEATABLE_READ|x|x|x|o|
|MySQL|2|TRANSACTION_READ_COMMITTED|x|x|o|o|
|Oracle|2| TRANSACTION_READ_COMMITTED |x|x|o|o|
|SQL Server|4| TRANSACTION_REPEATABLE_READ |x|x|x|o|
- ロストアップデート
- 後勝ちになって前の更新が失われる。
- ダーティリード
- コミットされていない更新も読込む。
- ノンリビータブルリード
- ある値を同一トランザクションから参照する際に、常に同じ値がかえるとは限らない。
- ファントムリード
- ノンリピータブルリードと似ているが、こちらは行単位で、さっきは存在していなかったものが見える。
MySQLのネクストキーロック
ただし、MySQLにはネクストキーロックというInnoDBのインデックスロック機構が働くので、一部ファントムリードが発生しない可能性もあります。
例)HogeTable
ikey | value |
---|---|
1 | hogevalue1 |
3 | hogevalue3 |
5 | hogevalue4 |
このとき、トランザクションT1とT2が以下のような振る舞いをしたとします(無理やり感はご容赦)。
START TRANSACTION;
SELECT count(*) FROM HogeTable;
SELECT value FROM HogeTable WHERE ikey = '3' FOR UPDATE;
/* なんか足し算とかで時間がかかる処理 */
UPDATE HogeTable SET value = 'newhogevalue3' WHERE ikey = '3';
SELECT count(*) FROM HogeTable;
COMMIT;
START TRANSACTION;
SELECT count(*) FROM HogeTable;
INSERT INTO HogeTable values ( '2', 'hogevalue2' );
SELECT count(*) FROM HogeTable;
COMMIT;
T1が開始され、「なんか足し算とかで時間がかかる処理」をしている間にT2が開始されたとします。そして、T1がDBを更新するより前にT2がCOMMIT
したとしても、T1のSELECT count(*)
ステートメントは「3」を返します。これはネクストキーロックによって、(negative_max-3]および(3-intmax)("("と")"は端点含まず、"["と"]"は端点含む)までインデックスロックがかかるためです。T1トランザクション内部的にロックがかかりますので、T2によるikey=2のINSERTの影響を受けません。T2はT2で独立して動作します(イメージ的にはロックしたタイミングで、インデックスのスナップショートデータを保持しているようなものです)。インデックスのみなので、他のトランザクションが値を変更してCOMMITした場合、その変更内容が読み取られます(READ_COMMITTED)。
START TRANSACTION;
INSERT INTO HogeTable values ( '0', 'hogevalue0' );
INSERT INTO HogeTable values ( '6', 'hogevalue6' );
SELECT count(*) FROM HogeTable;
UPDATE HogeTable SET value = 'newhogevalue1' WHERE ikey = '1';
UPDATE HogeTable SET value = 'newhogevalue5' WHERE ikey = '5';
COMMIT;
表に示した初期状態から同じように、T1が「なんか足し算とかで時間がかかる処理」をしている間にT2-αが開始されたとします。T2-αがCOMMITするとT2-αによるSELECT count(*)
ステートメントは「5」を返します。一方、T1ではやはり「3」が返ります。T1内でCOMMITする前に、SELECT * FROM HogeTable WHERE ikey = '1';
とかすると、ちゃんと"newhogevalue1"が、WHERE ikey = '5'
なら"newhogevalue5"がそれぞれ返ります。ただし、くどいようですがインデックスのみロックがかかっているので、追加した行は参照できません。T1内でSELECT * FROM HogeTable WHERE ikey = '0';
はnull
(Empty set)を返します。
話がだいぶそれたので、今回はこれくらいにしておきます。
-
Java 8 API Document
念のため補足。 ↩