As we need streaming BLOBs (we don't want to load entire images into memory), and EclipseLink by default doesn't handle streaming.
So I had to do a subclass of org.eclipse.persistence.platform.database.PostgreSQLPlatform. The following methods were implemented:
@Override public Object getObjectFromResultSet(ResultSet resultSet, int columnNumber, int type, AbstractSession session) throws SQLException { String name; if (type == Types.BIGINT) { // May be a number or an OID name = resultSet.getMetaData().getColumnTypeName(columnNumber); if ("OID".equalsIgnoreCase(name)) { return resultSet.getBlob(columnNumber); } } return super.getObjectFromResultSet(resultSet, columnNumber, type, session); } @Override public void setParameterValueInDatabaseCall(Object parameter, PreparedStatement statement, int index, AbstractSession session) throws SQLException { if (parameter instanceof DatabaseField) { DatabaseField field = (DatabaseField) parameter; if (Blob.class.equals(field.getType())) { statement.setBlob(index, (Blob) null); } else { super.setParameterValueInDatabaseCall(parameter, statement, index, session); } } else if (parameter instanceof Blob) { statement.setBlob(index, ((Blob) parameter)); } else { super.setParameterValueInDatabaseCall(parameter, statement, index, session); } } @Override public boolean shouldUseCustomModifyForCall(DatabaseField field) { if (Blob.class.equals(field.getType())) { return true; } return super.shouldUseCustomModifyForCall(field); } @Override @SuppressWarnings({ "rawtypes", "unchecked" }) protected Hashtable buildFieldTypes() { Hashtable types = super.buildFieldTypes(); types.put(Blob.class, new FieldTypeDefinition("OID", false)); return types; }
This way we can control: small binary data is mapped in entities via byte[]. Large binary data, via java.sql.Blob.
Then, to generate the schema:
EntityManagerFactoryImpl emf = (EntityManagerFactoryImpl) realEMF; DatabaseSessionImpl databaseSession = emf.getDatabaseSession(); StringWriter sw = new StringWriter(); SchemaManager schemaManager = new SchemaManager(databaseSession); schemaManager.outputDDLToWriter(sw); DefaultTableGenerator tableGenerator = new DefaultTableGenerator(databaseSession.getProject()) { @Override protected void resetFieldTypeForLOB(DirectToFieldMapping mapping) { // Hack to avoid the workaround for oracle 4k thin driver bug } }; TableCreator tableCreator = tableGenerator.generateDefaultTableCreator(); tableCreator.createTables(databaseSession, schemaManager); String script = sw.toString();
That DefaultTableGenerator inner class took me some hours debugging EclipseLink to figure out. The method comments says it is there to fix issues with oracle 4k thin driver. And it messed up the other use cases, as Blob was being handled as Byte[], and we want OID type specifically for Blobs.
Congratulations, Oracle! (facepalm)