diff --git a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java index 445f17216c..2ef3c09b55 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java @@ -42,6 +42,7 @@ package org.jooq.impl; import static java.lang.Boolean.TRUE; import static org.jooq.impl.Utils.DATA_LOCK_ROWS_FOR_UPDATE; +import static org.jooq.impl.Utils.recordFactory; import java.io.InputStream; import java.io.Reader; @@ -89,20 +90,21 @@ import org.jooq.tools.jdbc.JDBCUtils; */ class CursorImpl implements Cursor { - private static final JooqLogger log = JooqLogger.getLogger(CursorImpl.class); + private static final JooqLogger log = JooqLogger.getLogger(CursorImpl.class); - private final ExecuteContext ctx; - private final ExecuteListener listener; - private final Field[] fields; - private final boolean[] intern; - private final boolean keepResultSet; - private final boolean keepStatement; - private final Class type; - private boolean isClosed; + private final ExecuteContext ctx; + private final ExecuteListener listener; + private final Field[] fields; + private final boolean[] intern; + private final boolean keepResultSet; + private final boolean keepStatement; + private final Class type; + private final RecordFactory factory; + private boolean isClosed; - private transient CursorResultSet rs; - private transient Iterator iterator; - private transient int rows; + private transient CursorResultSet rs; + private transient Iterator iterator; + private transient int rows; @SuppressWarnings("unchecked") CursorImpl(ExecuteContext ctx, ExecuteListener listener, Field[] fields, int[] internIndexes, boolean keepStatement, boolean keepResultSet) { @@ -114,6 +116,7 @@ class CursorImpl implements Cursor { this.listener = (listener != null ? listener : new ExecuteListeners(ctx)); this.fields = fields; this.type = type; + this.factory = recordFactory(type, fields); this.keepStatement = keepStatement; this.keepResultSet = keepResultSet; this.rs = new CursorResultSet(); @@ -1410,7 +1413,7 @@ class CursorImpl implements Cursor { rs.updateRow(); } - record = Utils.newRecord(true, (Class) type, fields, ctx.configuration()) + record = Utils.newRecord(true, (RecordFactory) factory, ctx.configuration()) .operate(initialiser); rows++; diff --git a/jOOQ/src/main/java/org/jooq/impl/RecordFactory.java b/jOOQ/src/main/java/org/jooq/impl/RecordFactory.java new file mode 100644 index 0000000000..6b9d4f8402 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/RecordFactory.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * This work is dual-licensed + * - under the Apache Software License 2.0 (the "ASL") + * - under the jOOQ License and Maintenance Agreement (the "jOOQ License") + * ============================================================================= + * You may choose which license applies to you: + * + * - If you're using this work with Open Source databases, you may choose + * either ASL or jOOQ License. + * - If you're using this work with at least one commercial database, you must + * choose jOOQ License + * + * For more information, please visit http://www.jooq.org/licenses + * + * Apache Software License 2.0: + * ----------------------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * jOOQ License and Maintenance Agreement: + * ----------------------------------------------------------------------------- + * Data Geekery grants the Customer the non-exclusive, timely limited and + * non-transferable license to install and use the Software under the terms of + * the jOOQ License and Maintenance Agreement. + * + * This library is distributed with a LIMITED WARRANTY. See the jOOQ License + * and Maintenance Agreement for more details: http://www.jooq.org/licensing + */ +package org.jooq.impl; + +import org.jooq.Record; + +/** + * @author Lukas Eder + * @author Arnaud Roger + */ +interface RecordFactory { + + /** + * Create a new record with a given row type. + */ + R newInstance(); +} diff --git a/jOOQ/src/main/java/org/jooq/impl/Utils.java b/jOOQ/src/main/java/org/jooq/impl/Utils.java index a17fd2fba7..3834626ee9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Utils.java +++ b/jOOQ/src/main/java/org/jooq/impl/Utils.java @@ -69,6 +69,7 @@ import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER_ESCAPED; import static org.jooq.impl.Identifiers.QUOTE_START_DELIMITER; import static org.jooq.tools.reflect.Reflect.accessible; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.sql.Connection; @@ -434,24 +435,18 @@ final class Utils { } /** - * Create a new record + * Create a new record. */ - @SuppressWarnings({ "unchecked", "rawtypes" }) static final RecordDelegate newRecord(boolean fetched, Class type, Field[] fields, Configuration configuration) { + return newRecord(fetched, recordFactory(type, fields), configuration); + } + + /** + * Create a new record. + */ + static final RecordDelegate newRecord(boolean fetched, RecordFactory factory, Configuration configuration) { try { - R record; - - // An ad-hoc type resulting from a JOIN or arbitrary SELECT - if (type == RecordImpl.class || type == Record.class) { - record = (R) new RecordImpl(fields); - } - - // Any generated record - else { - - // [#919] Allow for accessing non-public constructors - record = Reflect.accessible(type.getDeclaredConstructor()).newInstance(); - } + R record = factory.newInstance(); // [#3300] Records that were fetched from the database if (record instanceof AbstractRecord) @@ -464,6 +459,47 @@ final class Utils { } } + /** + * Create a new record factory. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + static final RecordFactory recordFactory(final Class type, final Field[] fields) { + + // An ad-hoc type resulting from a JOIN or arbitrary SELECT + if (type == RecordImpl.class || type == Record.class) { + return new RecordFactory() { + @Override + public R newInstance() { + return (R) new RecordImpl(fields); + } + }; + } + + // Any generated record + else { + try { + + // [#919] Allow for accessing non-public constructors + final Constructor constructor = Reflect.accessible(type.getDeclaredConstructor()); + + return new RecordFactory() { + @Override + public R newInstance() { + try { + return constructor.newInstance(); + } + catch (Exception e) { + throw new IllegalStateException("Could not construct new record", e); + } + } + }; + } + catch (Exception e) { + throw new IllegalStateException("Could not construct new record", e); + } + } + } + /** * [#2700] [#3582] If a POJO attribute is NULL, but the column is NOT NULL * then we should let the database apply DEFAULT values