From 37b424edc8afe33ca6be31542f67bbc6a8585f29 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Sat, 22 Feb 2014 13:52:46 +0100 Subject: [PATCH] [#2573] Generate DAOs for tables with composite primary keys --- .../java/org/jooq/util/JavaGenerator.java | 56 ++++++++++---- .../jooq/configuration/lukas/h2/library.xml | 1 + jOOQ-test/src/org/jooq/test/H2Test.java | 40 ++++++++++ jOOQ/src/main/java/org/jooq/DAO.java | 8 +- jOOQ/src/main/java/org/jooq/impl/DAOImpl.java | 73 +++++++++++++------ 5 files changed, 137 insertions(+), 41 deletions(-) diff --git a/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java index 231396ec64..6d5cc3cdd4 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/util/JavaGenerator.java @@ -1351,25 +1351,32 @@ public class JavaGenerator extends AbstractGenerator { String pType = getStrategy().getFullJavaClassName(table, Mode.POJO); UniqueKeyDefinition key = table.getPrimaryKey(); - ColumnDefinition keyColumn = null; - - if (key != null) { - List columns = key.getKeyColumns(); - - if (columns.size() == 1) { - keyColumn = columns.get(0); - tType = getJavaType(keyColumn.getType()); - } - } - - // [#2573] Skip DAOs for tables that don't have 1-column-PKs (for now) - if (keyColumn == null) { + if (key == null) { log.info("Skipping DAO generation", getStrategy().getFileName(table, Mode.DAO)); return; } - else { - log.info("Generating DAO", getStrategy().getFileName(table, Mode.DAO)); + + List keyColumns = key.getKeyColumns(); + + if (keyColumns.size() == 1) { + tType = getJavaType(keyColumns.get(0).getType()); } + else if (keyColumns.size() <= Constants.MAX_ROW_DEGREE) { + String generics = ""; + String separator = ""; + + for (ColumnDefinition column : keyColumns) { + generics += separator + getJavaType(column.getType()); + separator = ", "; + } + + tType = Record.class.getName() + keyColumns.size() + "<" + generics + ">"; + } + else { + tType = Record.class.getName(); + } + + log.info("Generating DAO", getStrategy().getFileName(table, Mode.DAO)); JavaWriter out = new JavaWriter(getStrategy().getFile(table, Mode.DAO)); printPackage(out, table, Mode.DAO); @@ -1395,7 +1402,24 @@ public class JavaGenerator extends AbstractGenerator { // ------------------------------- out.tab(1).overrideInherit(); out.tab(1).println("protected %s getId(%s object) {", tType, pType); - out.tab(2).println("return object.%s();", getStrategy().getJavaGetterName(keyColumn, Mode.POJO)); + + if (keyColumns.size() == 1) { + out.tab(2).println("return object.%s();", getStrategy().getJavaGetterName(keyColumns.get(0), Mode.POJO)); + } + + // [#2574] This should be replaced by a call to a method on the target table's Key type + else { + String params = ""; + String separator = ""; + + for (ColumnDefinition column : keyColumns) { + params += separator + "object." + getStrategy().getJavaGetterName(column, Mode.POJO) + "()"; + separator = ", "; + } + + out.tab(2).println("return compositeKeyRecord(%s);", params); + } + out.tab(1).println("}"); for (ColumnDefinition column : table.getColumns()) { diff --git a/jOOQ-test/configuration/org/jooq/configuration/lukas/h2/library.xml b/jOOQ-test/configuration/org/jooq/configuration/lukas/h2/library.xml index a1e38b833a..c02ddf9848 100644 --- a/jOOQ-test/configuration/org/jooq/configuration/lukas/h2/library.xml +++ b/jOOQ-test/configuration/org/jooq/configuration/lukas/h2/library.xml @@ -129,6 +129,7 @@ false true false + true diff --git a/jOOQ-test/src/org/jooq/test/H2Test.java b/jOOQ-test/src/org/jooq/test/H2Test.java index 5461ae4da0..67090649ed 100644 --- a/jOOQ-test/src/org/jooq/test/H2Test.java +++ b/jOOQ-test/src/org/jooq/test/H2Test.java @@ -63,6 +63,7 @@ import static org.jooq.test.h2.generatedclasses.tables.TAuthor.FIRST_NAME; import static org.jooq.test.h2.generatedclasses.tables.TAuthor.LAST_NAME; import static org.jooq.test.h2.generatedclasses.tables.TBook.AUTHOR_ID; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import java.math.BigDecimal; import java.math.BigInteger; @@ -78,6 +79,7 @@ import org.jooq.DataType; import org.jooq.Field; import org.jooq.ForeignKey; import org.jooq.Record; +import org.jooq.Record2; import org.jooq.Result; import org.jooq.SQLDialect; import org.jooq.Table; @@ -113,7 +115,9 @@ import org.jooq.test.h2.generatedclasses.tables.T_785; import org.jooq.test.h2.generatedclasses.tables.VLibrary; import org.jooq.test.h2.generatedclasses.tables.daos.TAuthorDao; import org.jooq.test.h2.generatedclasses.tables.daos.T_2698Dao; +import org.jooq.test.h2.generatedclasses.tables.daos.XUnusedDao; import org.jooq.test.h2.generatedclasses.tables.pojos.T_2698; +import org.jooq.test.h2.generatedclasses.tables.pojos.XUnused; import org.jooq.test.h2.generatedclasses.tables.records.TArraysRecord; import org.jooq.test.h2.generatedclasses.tables.records.TAuthorRecord; import org.jooq.test.h2.generatedclasses.tables.records.TBookRecord; @@ -943,4 +947,40 @@ public class H2Test extends jOOQAbstractTest< assertEquals(1, (int) list.get(0).getId()); assertEquals(-1, (int) list.get(0).getXx()); } + + @SuppressWarnings("unchecked") + @Test + public void testH2DaoWithCompositeKey() throws Exception { + jOOQAbstractTest.reset = false; + + Record2 key1 = create().newRecord( + org.jooq.test.h2.generatedclasses.tables.XUnused.ID, + org.jooq.test.h2.generatedclasses.tables.XUnused.NAME); + + Record2 key2 = create().newRecord( + org.jooq.test.h2.generatedclasses.tables.XUnused.ID, + org.jooq.test.h2.generatedclasses.tables.XUnused.NAME); + key2.setValue(org.jooq.test.h2.generatedclasses.tables.XUnused.ID, 1); + key2.setValue(org.jooq.test.h2.generatedclasses.tables.XUnused.NAME, "name"); + + XUnusedDao dao = new XUnusedDao(create().configuration()); + dao.insert(new XUnused().setId(1).setName("name").setFields(1)); + assertNull(dao.findById(key1)); + + XUnused pojo = dao.findById(key2); + assertEquals(1, (int) pojo.getId()); + assertEquals("name", pojo.getName()); + assertEquals(1, (int) pojo.getFields()); + + pojo.setFields(2); + dao.update(pojo); + + pojo = dao.findById(key2); + assertEquals(1, (int) pojo.getId()); + assertEquals("name", pojo.getName()); + assertEquals(2, (int) pojo.getFields()); + + dao.deleteById(key2); + assertNull(dao.findById(key2)); + } } diff --git a/jOOQ/src/main/java/org/jooq/DAO.java b/jOOQ/src/main/java/org/jooq/DAO.java index 1b35c2db8f..f45fc0b60a 100644 --- a/jOOQ/src/main/java/org/jooq/DAO.java +++ b/jOOQ/src/main/java/org/jooq/DAO.java @@ -52,9 +52,11 @@ import org.jooq.exception.DataAccessException; * common actions on POJOs * * @author Lukas Eder - * @param The generic record type - * @param

The generic POJO type - * @param The generic primary key type + * @param The generic record type. + * @param

The generic POJO type. + * @param The generic primary key type. This is a regular + * <T> type for single-column keys, or a + * {@link Record} subtype for composite keys. */ public interface DAO, P, T> { diff --git a/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java b/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java index 2887459159..b577285e5c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java @@ -42,6 +42,7 @@ package org.jooq.impl; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; +import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.using; import java.util.ArrayList; @@ -52,8 +53,10 @@ import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.DAO; import org.jooq.Field; +import org.jooq.Record; import org.jooq.RecordMapper; import org.jooq.Table; +import org.jooq.TableField; import org.jooq.UniqueKey; import org.jooq.UpdatableRecord; @@ -185,7 +188,7 @@ public abstract class DAOImpl, P, T> implements DAO @Override public final void deleteById(Collection ids) { - Field pk = pk(); + Field[] pk = pk(); if (pk != null) { using(configuration).delete(table).where(equal(pk, ids)).execute(); @@ -199,7 +202,7 @@ public abstract class DAOImpl, P, T> implements DAO @Override public final boolean existsById(T id) { - Field pk = pk(); + Field[] pk = pk(); if (pk != null) { return using(configuration) @@ -231,7 +234,7 @@ public abstract class DAOImpl, P, T> implements DAO @Override public final P findById(T id) { - Field pk = pk(); + Field[] pk = pk(); R record = null; if (pk != null) { @@ -279,45 +282,71 @@ public abstract class DAOImpl, P, T> implements DAO protected abstract T getId(P object); + @SuppressWarnings("unchecked") + protected final T compositeKeyRecord(Object... values) { + UniqueKey key = table.getPrimaryKey(); + if (key == null) + return null; + + TableField[] fields = (TableField[]) key.getFieldsArray(); + Record result = DSL.using(configuration) + .newRecord(fields); + + for (int i = 0; i < values.length; i++) { + result.setValue(fields[i], fields[i].getDataType().convert(values[i])); + } + + return (T) result; + } + // ------------------------------------------------------------------------ // XXX: Private utility methods // ------------------------------------------------------------------------ - private final Condition equal(Field pk, T id) { - return pk.equal(pk.getDataType().convert(id)); - } - - private final Condition equal(Field pk, Collection ids) { - if (ids.size() == 1) { - return equal(pk, ids.iterator().next()); + @SuppressWarnings("unchecked") + private final Condition equal(Field[] pk, T id) { + if (pk.length == 1) { + return ((Field) pk[0]).equal(pk[0].getDataType().convert(id)); } + + // [#2573] Composite key T types are of type Record[N] else { - return pk.in(pk.getDataType().convert(ids)); + return row(pk).equal((Record) id); } } - private final Field pk() { - UniqueKey key = table.getPrimaryKey(); - - if (key != null) { - if (key.getFields().size() == 1) { - return key.getFields().get(0); + @SuppressWarnings("unchecked") + private final Condition equal(Field[] pk, Collection ids) { + if (pk.length == 1) { + if (ids.size() == 1) { + return equal(pk, ids.iterator().next()); + } + else { + return ((Field) pk[0]).in(pk[0].getDataType().convert(ids)); } } - return null; + // [#2573] Composite key T types are of type Record[N] + else { + return row(pk).in(ids.toArray(new Record[ids.size()])); + } + } + + private final Field[] pk() { + UniqueKey key = table.getPrimaryKey(); + return key == null ? null : key.getFieldsArray(); } private final List records(Collection

objects, boolean forUpdate) { List result = new ArrayList(); - Field pk = pk(); + Field[] pk = pk(); for (P object : objects) { R record = using(configuration).newRecord(table, object); - if (forUpdate && pk != null) { - ((AbstractRecord) record).getValue0(pk).setChanged(false); - } + if (forUpdate && pk != null) + for (Field field : pk) + ((AbstractRecord) record).getValue0(field).setChanged(false); result.add(record); }