001package com.randomnoun.common.spring; 002 003/* (c) 2013 randomnoun. All Rights Reserved. This work is licensed under a 004 * BSD Simplified License. (http://www.randomnoun.com/bsd-simplified.html) 005 */ 006 007import java.io.*; 008import java.sql.*; 009import java.util.*; 010 011import javax.sql.*; 012 013import org.apache.log4j.Logger; 014import org.springframework.jdbc.core.*; 015import org.springframework.jdbc.support.DatabaseMetaDataCallback; 016import org.springframework.jdbc.support.JdbcUtils; 017import org.springframework.jdbc.support.MetaDataAccessException; 018import org.springframework.jdbc.support.lob.DefaultLobHandler; 019import org.springframework.jdbc.support.lob.LobHandler; 020 021import com.randomnoun.common.StreamUtil; 022import com.randomnoun.common.spring.ClobRowMapper; 023 024/** 025 * This class is intended to replace the standard RowMapper provided by Spring 026 * to allow CLOBs to be treated as String objects transparently (i.e. the Map 027 * which represents a row returned by this class will not contain any 028 * oracle.sql.CLOB objects). 029 * 030 * <p>LobHandlers aren't required in recent Oracle drivers, so this entire class is 031 * probably obsolete these days. 032 * 033 * 034 * @author knoxg 035 */ 036public class ClobRowMapper implements RowMapper<Map<String, Object>> 037{ 038 039 /** Logger for this class */ 040 public final Logger logger = Logger.getLogger(ClobRowMapper.class); 041 042 /** Oracle Large OBject handler. */ 043 final LobHandler lobHandler; 044 045 public String detectDatabase(DataSource ds) { 046 logger.debug("Looking up default SQLErrorCodes for DataSource"); 047 String dbName = "unknown"; 048 049 try { 050 @SuppressWarnings("unchecked") 051 Map<String, Object> dbmdInfo = (Map<String, Object>) JdbcUtils.extractDatabaseMetaData(ds, new DatabaseMetaDataCallback() { 052 public Object processMetaData(DatabaseMetaData dbmd) throws SQLException { 053 Map<String, Object> info = new HashMap<String, Object>(2); 054 if (dbmd != null) { 055 info.put("DatabaseProductName", dbmd.getDatabaseProductName()); 056 info.put("DriverVersion", dbmd.getDriverVersion()); 057 } 058 return info; 059 } 060 }); 061 if (dbmdInfo != null) { 062 063 // should always be the case outside of test environments 064 dbName = (String) dbmdInfo.get("DatabaseProductName"); 065 String driverVersion = (String) dbmdInfo.get("DriverVersion"); 066 logger.debug("Found dbName='" + dbName + "', driverVerson='" + driverVersion + "'"); 067 068 if (dbName != null && dbName.startsWith("DB2")) { 069 dbName = "DB2"; 070 } 071 /* 072 if (dbName != null) { 073 this.dataSourceProductName.put(ds, dbName); 074 logger.info("Database Product Name is " + dbName); 075 logger.info("Driver Version is " + driverVersion); 076 SQLErrorCodes sec = (SQLErrorCodes) this.rdbmsErrorCodes.get(dbName); 077 if (sec != null) { 078 return sec; 079 } 080 logger.info("Error Codes for " + dbName + " not found"); 081 } 082 */ 083 } 084 } 085 catch (MetaDataAccessException ex) { 086 logger.warn("Error while getting database metadata", ex); 087 } 088 089 // fallback is to return an empty ErrorCodes instance 090 logger.debug("Returning dbName='" + dbName + "'"); 091 return dbName; 092 } 093 094 095 /** 096 * Creates a new ClobRowMapper object. 097 */ 098 public ClobRowMapper(JdbcTemplate jt) 099 { 100 lobHandler = new DefaultLobHandler(); 101 /* 102 DataSource ds = jt.getDataSource(); 103 try { 104 if (detectDatabase(ds).equals("DB2")) { 105 lobHandler = new DefaultLobHandler(); 106 } else { 107 // lobHandler = new OracleLobHandler(); 108 } 109 } catch (Exception e) { 110 throw new RuntimeException("Could not instantiate lobHandler"); 111 } 112 */ 113 } 114 115 /** Map rows to a disconnected HashMap representation */ 116 public Map<String, Object> mapRow(ResultSet rs, int rowNum) 117 throws SQLException 118 { 119 Map<String, Object> row = new HashMap<String, Object>(); 120 String key; 121 Object value; 122 123 ResultSetMetaData metaData = rs.getMetaData(); 124 int columnCount = metaData.getColumnCount(); 125 126 for (int i = 1; i <= columnCount; i++) { 127 // key.toUpperCase required for SqlServer; DB2 & Oracle will automatically 128 // do this anyway 129 key = metaData.getColumnLabel(i).toUpperCase(); 130 value = rs.getObject(i); 131 if (value != null && value.getClass().getName().equals("oracle.sql.CLOB")) { 132 value = lobHandler.getClobAsString(rs, i); 133 } else if (value instanceof java.sql.Clob) { 134 Clob clob = (Clob) value; 135 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 136 try { 137 StreamUtil.copyStream(clob.getAsciiStream(), baos, 1024); 138 } catch (IOException ioe) { 139 throw (SQLException) new SQLException("IO error transferring CLOB").initCause(ioe); 140 } 141 142 value = baos.toString(); 143 } 144 row.put(key, value); 145 } 146 147 return row; 148 } 149}