简体   繁体   中英

What is the correct way to use of isWrapperFor and unwrap functions in JDBC?

I would like to know the correct way to validate isWrapperFor and unwrap functions in JDBC. Here HIRAConnection and the standard Connection classes are used.

HIRAConnection hiraCon1 = (HIRAConnection) ds.getConnection();
Connection conn = ds.getConnection();
if (hiraCon1 instanceof Wrapper) {
    // try to use java 6 unwrapping
    try {
        Wrapper w = conn;
        if (hiraCon1.isWrapperFor(Connection.class)) {
            hiraCon1 = conn.unwrap(HIRAConnection.class); 
            hiraCon1= hiraCon1.unwrap(HIRAConnection.class);
            hiraCon1= ds.unwrap(HIRAConnection.class);//returns SQLException   
        }
        if (hiraCon1.isWrapperFor(HIRAConnection.class)) {
            hiraCon1 = conn.unwrap(HIRAConnection.class);
            hiraCon1 = hiraCon1.unwrap(HIRAConnection.class);    
        }
        if (conn.isWrapperFor(com.hira.HIRAConnection.class)) {          
            hiraCon1 = conn.unwrap(com.hira.HIRAConnection.class);
        } 
        if (conn.isWrapperFor(Connection.class)) {          
            hiraCon1 = conn.unwrap(com.hira.HIRAConnection.class);
        } 
    } catch (Throwable t) {
        System.out.println("Failed to unwrap connection using java 6 facilities");
    }
}

The correct way to use java.sql.Wrapper is documented in its javadoc.

For isWrapperFor(Class<?> iface) :

Returns true if this either implements the interface argument or is directly or indirectly a wrapper for an object that does. Returns false otherwise. If this implements the interface then return true, else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped object. If this does not implement the interface and is not a wrapper, return false. This method should be implemented as a low-cost operation compared to unwrap so that callers can use this method to avoid expensive unwrap calls that may fail. If this method returns true then calling unwrap with the same argument should succeed.

Parameters :
iface - a Class defining an interface.
Returns :
true if this implements the interface or directly or indirectly wraps an object that does.

And for unwrap(Class<T> iface) :

Returns an object that implements the given interface to allow access to non-standard methods, or standard methods not exposed by the proxy. If the receiver implements the interface then the result is the receiver or a proxy for the receiver. If the receiver is a wrapper and the wrapped object implements the interface then the result is the wrapped object or a proxy for the wrapped object. Otherwise return the the result of calling unwrap recursively on the wrapped object or a proxy for that result. If the receiver is not a wrapper and does not implement the interface, then an SQLException is thrown.

Type Parameters :
T - the type of the class modeled by this Class object
Parameters :
iface - A Class defining an interface that the result must implement.
Returns :
an object that implements the interface. May be a proxy for the actual implementing object.
Throws :
SQLException - If no object found that implements the interface

In other words, you can first check using wrapperFor if the wrapper can unwrap to the interface, and then you can use unwrap to really unwrap to that interface. Be aware that the specification only mentions support to unwrap to interface, so unwrapping to concrete classes may not actually be possible.

Whether this works, depends on the driver used (not all drivers support unwrapping or maybe they have nothing useful to unwrap), and if you are using a connection pool library, then it is entirely possible that it doesn't allow you to unwrap to - for example - the underlying connection, because doing that could allow you to circumvent or break certain limits and requirements of the connection pool.

So the correct way to use wrapping would be:

Connection conn = ds.getConnection();
if (conn.isWrapperFor(HiraConnection.class)) {
    HIRAConnection hiraCon1 = conn.unwrap(HiraConnection.class);
    // use hiraCon1...
)

However if HiraConnection is a concrete class instead of an interface, this might not work. And unwrapping will usually also lead to brittle code. It is usually better to avoid relying on driver specific interface unless absolutely necessary.

Some remarks on the code in your question:

  • If you know HIRAConnection hiraCon1 = (HIRAConnection) ds.getConnection(); works, then you don't need to unwrap at all.
  • Checking for hiraCon1 instanceof Wrapper is a useless check, because if HIRAConnection implements java.sql.Connection , then it will always implement java.sql.Wrapper (and otherwise running your code would yield a ClassNotFoundException for java.sql.Wrapper meaning you ran it on Java 5 or lower).
  • Checking hiraCon1.isWrapperFor(Connection.class) and then unwrapping conn to HIRAConnection is unsafe and makes no sense. If you want to unwrap conn to HIRAConnection , then you need to use conn.isWrapperFor(HiraConnection.class)
  • It is not unexpected that hiraCon1= ds.unwrap(HIRAConnection.class); throws an SQLException : it is unlikely that a javax.sql.DataSource implementation considers itself a wrapper for connections.
  • Checking hiraCon1.isWrapperFor(HIRAConnection.class) is a bit odd: you already know hiraCon1 is a HIRAConnection
  • As previously mentioned, unwrapping conn after checking hiraCon1 makes no sense.
  • Checking conn.isWrapperFor(Connection.class) is a bit odd: you already know conn is a Connection
  • Checking conn.isWrapperFor(Connection.class) and then using conn.unwrap(com.hira.HIRAConnection.class) is unsafe, as you have only checked whether conn unwraps to Connection , not to HIRAConnection .

They are not really for use by applications, except as noted by @MarkRotteveel below. They are used more by JDBC driver authors, for example the Apache DBCP.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM