[#2573] Generate DAOs for tables with composite primary keys

This commit is contained in:
Lukas Eder 2014-02-22 13:52:46 +01:00
parent 1f5f2f4ce6
commit 37b424edc8
5 changed files with 137 additions and 41 deletions

View File

@ -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<ColumnDefinition> 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<ColumnDefinition> 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()) {

View File

@ -129,6 +129,7 @@
<generatedAnnotation>false</generatedAnnotation>
<interfaces>true</interfaces>
<jpaAnnotations>false</jpaAnnotations>
<fluentSetters>true</fluentSetters>
</generate>
<target>
<packageName>

View File

@ -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<Integer, String> key1 = create().newRecord(
org.jooq.test.h2.generatedclasses.tables.XUnused.ID,
org.jooq.test.h2.generatedclasses.tables.XUnused.NAME);
Record2<Integer, String> 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));
}
}

View File

@ -52,9 +52,11 @@ import org.jooq.exception.DataAccessException;
* common actions on POJOs
*
* @author Lukas Eder
* @param <R> The generic record type
* @param <P> The generic POJO type
* @param <T> The generic primary key type
* @param <R> The generic record type.
* @param <P> The generic POJO type.
* @param <T> The generic primary key type. This is a regular
* <code>&lt;T></code> type for single-column keys, or a
* {@link Record} subtype for composite keys.
*/
public interface DAO<R extends TableRecord<R>, P, T> {

View File

@ -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<R extends UpdatableRecord<R>, P, T> implements DAO
@Override
public final void deleteById(Collection<T> 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<R extends UpdatableRecord<R>, 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<R extends UpdatableRecord<R>, 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<R extends UpdatableRecord<R>, P, T> implements DAO
protected abstract T getId(P object);
@SuppressWarnings("unchecked")
protected final T compositeKeyRecord(Object... values) {
UniqueKey<R> key = table.getPrimaryKey();
if (key == null)
return null;
TableField<R, Object>[] fields = (TableField<R, Object>[]) 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 <U> Condition equal(Field<U> pk, T id) {
return pk.equal(pk.getDataType().convert(id));
}
private final <U> Condition equal(Field<U> pk, Collection<T> 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<Object>) 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<T> ids) {
if (pk.length == 1) {
if (ids.size() == 1) {
return equal(pk, ids.iterator().next());
}
else {
return ((Field<Object>) 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<R> records(Collection<P> objects, boolean forUpdate) {
List<R> result = new ArrayList<R>();
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);
}