[#2573] Generate DAOs for tables with composite primary keys
This commit is contained in:
parent
1f5f2f4ce6
commit
37b424edc8
@ -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()) {
|
||||
|
||||
@ -129,6 +129,7 @@
|
||||
<generatedAnnotation>false</generatedAnnotation>
|
||||
<interfaces>true</interfaces>
|
||||
<jpaAnnotations>false</jpaAnnotations>
|
||||
<fluentSetters>true</fluentSetters>
|
||||
</generate>
|
||||
<target>
|
||||
<packageName>
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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><T></code> type for single-column keys, or a
|
||||
* {@link Record} subtype for composite keys.
|
||||
*/
|
||||
public interface DAO<R extends TableRecord<R>, P, T> {
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user