[#6517] JPADatabase should map JPA AttributeConverter types to generated jOOQ Converter

This commit is contained in:
lukaseder 2017-08-24 12:28:02 +02:00
parent 563e7c7938
commit b1a49eff15
7 changed files with 104 additions and 46 deletions

View File

@ -138,13 +138,6 @@
</property>
</properties>
<includes>.*</includes>
<forcedTypes>
<forcedType>
<userType>java.time.Year</userType>
<converter>org.jooq.Converter.of(Integer.class, Year.class, new org.jooq.example.jpa.converters.YearConverter()::convertToEntityAttribute, new org.jooq.example.jpa.converters.YearConverter()::convertToDatabaseColumn)</converter>
<expression>RELEASE_YEAR</expression>
</forcedType>
</forcedTypes>
</database>
<generate>
</generate>

View File

@ -33,7 +33,7 @@ import org.jooq.impl.SchemaImpl;
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
public class Public extends SchemaImpl {
private static final long serialVersionUID = 1252977499;
private static final long serialVersionUID = 897816362;
/**
* The reference instance of <code>PUBLIC</code>
@ -85,9 +85,9 @@ public class Public extends SchemaImpl {
private final List<Sequence<?>> getSequences0() {
return Arrays.<Sequence<?>>asList(
Sequences.SYSTEM_SEQUENCE_5B4EAFE5_365F_441D_BC0D_72B0BA27E6CC,
Sequences.SYSTEM_SEQUENCE_65506620_5C73_4217_9913_7AB008A3F158,
Sequences.SYSTEM_SEQUENCE_805FDF72_C4BB_4AA3_86DB_0EA4233F1A7C);
Sequences.SYSTEM_SEQUENCE_89CD2855_22EA_4F63_95A8_18A79F6EC782,
Sequences.SYSTEM_SEQUENCE_BE504C6E_CD12_493D_BEC3_FE5DEDA7FE1A,
Sequences.SYSTEM_SEQUENCE_C6201643_1CF6_4072_8443_E47AC449BE9D);
}
@Override

View File

@ -24,17 +24,17 @@ import org.jooq.impl.SequenceImpl;
public class Sequences {
/**
* The sequence <code>PUBLIC.SYSTEM_SEQUENCE_5B4EAFE5_365F_441D_BC0D_72B0BA27E6CC</code>
* The sequence <code>PUBLIC.SYSTEM_SEQUENCE_89CD2855_22EA_4F63_95A8_18A79F6EC782</code>
*/
public static final Sequence<Long> SYSTEM_SEQUENCE_5B4EAFE5_365F_441D_BC0D_72B0BA27E6CC = new SequenceImpl<Long>("SYSTEM_SEQUENCE_5B4EAFE5_365F_441D_BC0D_72B0BA27E6CC", Public.PUBLIC, org.jooq.impl.SQLDataType.BIGINT);
public static final Sequence<Long> SYSTEM_SEQUENCE_89CD2855_22EA_4F63_95A8_18A79F6EC782 = new SequenceImpl<Long>("SYSTEM_SEQUENCE_89CD2855_22EA_4F63_95A8_18A79F6EC782", Public.PUBLIC, org.jooq.impl.SQLDataType.BIGINT);
/**
* The sequence <code>PUBLIC.SYSTEM_SEQUENCE_65506620_5C73_4217_9913_7AB008A3F158</code>
* The sequence <code>PUBLIC.SYSTEM_SEQUENCE_BE504C6E_CD12_493D_BEC3_FE5DEDA7FE1A</code>
*/
public static final Sequence<Long> SYSTEM_SEQUENCE_65506620_5C73_4217_9913_7AB008A3F158 = new SequenceImpl<Long>("SYSTEM_SEQUENCE_65506620_5C73_4217_9913_7AB008A3F158", Public.PUBLIC, org.jooq.impl.SQLDataType.BIGINT);
public static final Sequence<Long> SYSTEM_SEQUENCE_BE504C6E_CD12_493D_BEC3_FE5DEDA7FE1A = new SequenceImpl<Long>("SYSTEM_SEQUENCE_BE504C6E_CD12_493D_BEC3_FE5DEDA7FE1A", Public.PUBLIC, org.jooq.impl.SQLDataType.BIGINT);
/**
* The sequence <code>PUBLIC.SYSTEM_SEQUENCE_805FDF72_C4BB_4AA3_86DB_0EA4233F1A7C</code>
* The sequence <code>PUBLIC.SYSTEM_SEQUENCE_C6201643_1CF6_4072_8443_E47AC449BE9D</code>
*/
public static final Sequence<Long> SYSTEM_SEQUENCE_805FDF72_C4BB_4AA3_86DB_0EA4233F1A7C = new SequenceImpl<Long>("SYSTEM_SEQUENCE_805FDF72_C4BB_4AA3_86DB_0EA4233F1A7C", Public.PUBLIC, org.jooq.impl.SQLDataType.BIGINT);
public static final Sequence<Long> SYSTEM_SEQUENCE_C6201643_1CF6_4072_8443_E47AC449BE9D = new SequenceImpl<Long>("SYSTEM_SEQUENCE_C6201643_1CF6_4072_8443_E47AC449BE9D", Public.PUBLIC, org.jooq.impl.SQLDataType.BIGINT);
}

View File

@ -40,7 +40,7 @@ import org.jooq.impl.TableImpl;
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
public class Film extends TableImpl<FilmRecord> {
private static final long serialVersionUID = 1113845742;
private static final long serialVersionUID = 1030256935;
/**
* The reference instance of <code>PUBLIC.FILM</code>
@ -68,7 +68,7 @@ public class Film extends TableImpl<FilmRecord> {
/**
* The column <code>PUBLIC.FILM.RELEASE_YEAR</code>.
*/
public final TableField<FilmRecord, Year> RELEASE_YEAR = createField("RELEASE_YEAR", org.jooq.impl.SQLDataType.INTEGER, this, "", org.jooq.Converter.of(Integer.class, Year.class, new org.jooq.example.jpa.converters.YearConverter()::convertToEntityAttribute, new org.jooq.example.jpa.converters.YearConverter()::convertToDatabaseColumn));
public final TableField<FilmRecord, Year> RELEASE_YEAR = createField("RELEASE_YEAR", org.jooq.impl.SQLDataType.INTEGER, this, "", new org.jooq.impl.JPAConverter(org.jooq.example.jpa.converters.YearConverter.class));
/**
* The column <code>PUBLIC.FILM.TITLE</code>.

View File

@ -17673,6 +17673,14 @@ public class Book {
<key>packages</key>
<value>com.example.entities</value>
</property>
<!-- Whether JPA 2.1 AttributeConverters should be auto-mapped to jOOQ Converters.
Custom <forcedType/> configurations will have a higher priority than these auto-mapped converters.
This defaults to true. -->
<property>
<key>use-attribute-converters</key>
<value>true</value>
</property>
</properties>
</database>
</generator>]]></xml><html>

View File

@ -37,22 +37,30 @@ package org.jooq.util.jpa;
import static org.jooq.tools.StringUtils.defaultIfBlank;
import static org.jooq.tools.StringUtils.isBlank;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import javax.persistence.AttributeConverter;
import javax.persistence.Entity;
import org.jooq.DSLContext;
import org.jooq.Name;
import org.jooq.SQLDialect;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
import org.jooq.impl.JPAConverter;
import org.jooq.tools.JooqLogger;
import org.jooq.util.SchemaDefinition;
import org.jooq.util.h2.H2Database;
import org.jooq.util.jaxb.ForcedType;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -84,11 +92,11 @@ import org.springframework.core.type.filter.AnnotationTypeFilter;
*/
public class JPADatabase extends H2Database {
private static final JooqLogger log = JooqLogger.getLogger(JPADatabase.class);
static final String HIBERNATE_DIALECT = SQLDialect.H2.thirdParty().hibernateDialect();
static final JooqLogger log = JooqLogger.getLogger(JPADatabase.class);
private Connection connection;
private Connection connection;
@SuppressWarnings("serial")
@Override
protected DSLContext create0() {
if (connection == null) {
@ -99,38 +107,19 @@ public class JPADatabase extends H2Database {
log.warn("No packages defined", "It is highly recommended that you provide explicit packages to scan");
}
boolean useAttributeConverters = Boolean.valueOf(getProperties().getProperty("use-attribute-converters", "true"));
try {
connection = DriverManager.getConnection("jdbc:h2:mem:jooq-meta-extensions", "sa", "");
MetadataSources metadata = new MetadataSources(
new StandardServiceRegistryBuilder()
.applySetting("hibernate.dialect", "org.hibernate.dialect.H2Dialect")
.applySetting("hibernate.dialect", HIBERNATE_DIALECT)
.applySetting("javax.persistence.schema-generation-connection", connection)
.applySetting("javax.persistence.create-database-schemas", true)
// [#5607] JPADatabase causes warnings - This prevents them
.applySetting(AvailableSettings.CONNECTION_PROVIDER, new ConnectionProvider() {
@SuppressWarnings("rawtypes")
@Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}
@Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}
@Override
public Connection getConnection() {
return connection;
}
@Override
public void closeConnection(Connection conn) throws SQLException {}
@Override
public boolean supportsAggressiveRelease() {
return true;
}
})
.applySetting(AvailableSettings.CONNECTION_PROVIDER, connectionProvider())
.build()
);
@ -154,6 +143,9 @@ public class JPADatabase extends H2Database {
// Hibernate 5.2 broke 5.0 API again. Here's how to do this now:
SchemaExport export = new SchemaExport();
export.create(EnumSet.of(TargetType.DATABASE), metadata.buildMetadata());
if (useAttributeConverters)
loadAttributeConverters(metadata.getAnnotatedClasses());
}
catch (Exception e) {
throw new DataAccessException("Error while exporting schema", e);
@ -163,6 +155,71 @@ public class JPADatabase extends H2Database {
return DSL.using(connection);
}
@SuppressWarnings("serial")
ConnectionProvider connectionProvider() {
return new ConnectionProvider() {
@SuppressWarnings("rawtypes")
@Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}
@Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}
@Override
public Connection getConnection() {
return connection;
}
@Override
public void closeConnection(Connection conn) {}
@Override
public boolean supportsAggressiveRelease() {
return true;
}
};
}
private final void loadAttributeConverters(Collection<? extends Class<?>> classes) {
try {
AttributeConverterExtractor extractor = new AttributeConverterExtractor(this, classes);
attributesLoop:
for (Entry<Name, AttributeConverter<?, ?>> entry : extractor.extract().entrySet()) {
Class<?> convertToEntityAttribute = null;
for (Method method : entry.getValue().getClass().getMethods())
if ("convertToEntityAttribute".equals(method.getName()))
convertToEntityAttribute = method.getReturnType();
if (convertToEntityAttribute == null) {
log.info("AttributeConverter", "Cannot use AttributeConverter: " + entry.getValue().getClass().getName());
continue attributesLoop;
}
// Tables can be fully or partially or not at all qualified. Let's just accept any prefix
// to the available qualification
String regex = "(.*?\\.)?" + entry.getKey().unquotedName().toString().replace(".", "\\.");
ForcedType forcedType = new ForcedType()
.withExpression("(?i:" + regex + ")")
.withUserType(convertToEntityAttribute.getName())
.withConverter(String.format("new %s(%s.class)",
JPAConverter.class.getName(),
entry.getValue().getClass().getName()
));
log.info("AttributeConverter", "Configuring JPA AttributeConverter: " + toString(forcedType));
getConfiguredForcedTypes().add(forcedType);
}
}
// AttributeConverter is part of JPA 2.1. Older JPA providers may not have this type, yet
catch (NoClassDefFoundError e) {
log.info("AttributeConverter", "Cannot load AttributeConverters: " + e.getMessage());
}
}
@Override
protected List<SchemaDefinition> getSchemata0() throws SQLException {
List<SchemaDefinition> result = new ArrayList<SchemaDefinition>(super.getSchemata0());

View File

@ -997,7 +997,7 @@ public abstract class AbstractDatabase implements Database {
}
@SuppressWarnings({ "unchecked", "rawtypes" })
static final String toString(final Object object) {
protected static final String toString(final Object object) {
final StringWriter writer = new StringWriter();
try {