Skip to content

Commit 1dccdff

Browse files
authored
#3664 Fix / support for extra JoinColumns on ManyToOne (#3668)
* #3664 Fix / support for extra JoinColumns on ManyToOne Allows for extra JoinColumns on ManyToOne. The extra JoinColumn(s) are expected to be useful for the case of table partitioning where the extra join column is used to partition the table. In the test case, the partition column would be the org_id column and common to both tables (same partition key). * #3664 Fix / support for extra JoinColumns on ManyToOne Allows for extra JoinColumns on ManyToOne. The extra JoinColumn(s) are expected to be useful for the case of table partitioning where the extra join column is used to partition the table. In the test case, the partition column would be the org_id column and common to both tables (same partition key). * #3664 Extend test * #3664 Tidy up test
1 parent 5f74a53 commit 1dccdff

File tree

6 files changed

+149
-18
lines changed

6 files changed

+149
-18
lines changed

ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssoc.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -436,14 +436,15 @@ ImportedId createImportedId(BeanPropertyAssoc<?> owner, BeanDescriptor<?> target
436436
}
437437
TableJoinColumn[] cols = join.columns();
438438
if (!idProp.isEmbedded()) {
439-
// simple single scalar id
440-
if (cols.length != 1) {
441-
CoreLog.log.log(ERROR, "No Imported Id column for {0} in table {1}", idProp, join.getTable());
442-
return null;
443-
} else {
444-
BeanProperty[] idProps = {idProp};
445-
return createImportedScalar(owner, cols[0], idProps, others);
439+
// simple single scalar id, match on the foreign column, allow extra TableJoinColumn for #3664
440+
String matchColumn = idProp.dbColumn();
441+
for (TableJoinColumn col : cols) {
442+
if (matchColumn.equals(col.getForeignDbColumn())) {
443+
return createImportedScalar(owner, col, new BeanProperty[]{idProp}, others);
444+
}
446445
}
446+
CoreLog.log.log(ERROR, "No Imported Id column for {0} in table {1}", idProp, join.getTable());
447+
return null;
447448
} else {
448449
// embedded id
449450
BeanPropertyAssocOne<?> embProp = (BeanPropertyAssocOne<?>) idProp;

ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,6 @@ private void initialiseAssocOne(String embeddedPrefix) {
128128
throw new PersistenceException("Cannot find imported id for " + fullName() + " from " + targetDescriptor
129129
+ ". If using native-image, possibly missing reflect-config for the Id property.");
130130
}
131-
if (importedId.isScalar()) {
132-
// limit JoinColumn mapping to the @Id / primary key
133-
TableJoinColumn[] columns = tableJoin.columns();
134-
String foreignJoinColumn = columns[0].getForeignDbColumn();
135-
String foreignIdColumn = targetDescriptor.idProperty().dbColumn();
136-
if (!foreignJoinColumn.equalsIgnoreCase(foreignIdColumn)) {
137-
throw new PersistenceException("Mapping limitation - @JoinColumn on " + fullName() + " needs to map to a primary key as per Issue #529 "
138-
+ " - joining to " + foreignJoinColumn + " and not " + foreignIdColumn);
139-
}
140-
}
141131
} else {
142132
exportedProperties = createExported();
143133
String delStmt = "delete from " + targetDescriptor.baseTable() + " where ";

ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/model/build/ModelBuildPropertyVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public void visitOneImported(BeanPropertyAssocOne<?> p) {
195195
String dbCol = column.getLocalDbColumn();
196196
BeanProperty importedProperty = p.findMatchImport(dbCol);
197197
if (importedProperty == null) {
198-
throw new RuntimeException("Imported BeanProperty not found?");
198+
continue;
199199
}
200200
String columnDefn = ctx.getColumnDefn(importedProperty, true);
201201
String refColumn = importedProperty.dbColumn();
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.tests.model.m2o;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.Id;
6+
7+
@Entity
8+
public class MTJOrder {
9+
10+
@Id
11+
private long id;
12+
13+
@Column(name = "org_id")
14+
private long orgId;
15+
16+
@Column
17+
private String other;
18+
19+
public Long id() {
20+
return id;
21+
}
22+
23+
public MTJOrder setId(Long id) {
24+
this.id = id;
25+
return this;
26+
}
27+
28+
public Long orgId() {
29+
return orgId;
30+
}
31+
32+
public MTJOrder setOrgId(Long orgId) {
33+
this.orgId = orgId;
34+
return this;
35+
}
36+
37+
public String other() {
38+
return other;
39+
}
40+
41+
public MTJOrder setOther(String other) {
42+
this.other = other;
43+
return this;
44+
}
45+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.tests.model.m2o;
2+
3+
import jakarta.persistence.*;
4+
5+
@Entity
6+
public class MTJTrans {
7+
8+
@Id
9+
private long id;
10+
11+
@Column(name = "org_id")
12+
private long orgId;
13+
14+
@ManyToOne
15+
@JoinColumns({
16+
@JoinColumn(name = "org_id", referencedColumnName = "org_id"), // extra join column, not strictly needed
17+
@JoinColumn(name = "order_id", referencedColumnName = "id")
18+
})
19+
private MTJOrder order;
20+
21+
public Long id() {
22+
return id;
23+
}
24+
25+
public MTJTrans setId(Long id) {
26+
this.id = id;
27+
return this;
28+
}
29+
30+
public Long orgId() {
31+
return orgId;
32+
}
33+
34+
public MTJTrans setOrgId(Long orgId) {
35+
this.orgId = orgId;
36+
return this;
37+
}
38+
39+
public MTJOrder order() {
40+
return order;
41+
}
42+
43+
public MTJTrans setOrder(MTJOrder order) {
44+
this.order = order;
45+
return this;
46+
}
47+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.tests.model.m2o;
2+
3+
import io.ebean.DB;
4+
import io.ebean.test.LoggedSql;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.util.List;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
class TestMTJoinColumns {
12+
13+
@Test
14+
void test() {
15+
MTJTrans parent = new MTJTrans();
16+
parent.setOrgId(51L);
17+
18+
DB.save(parent);
19+
20+
MTJTrans found = DB.find(MTJTrans.class)
21+
.setId(parent.id())
22+
.fetch("order")
23+
.findOne();
24+
25+
assertThat(found.order()).isNull();
26+
27+
var order = new MTJOrder()
28+
.setOrgId(51L)
29+
.setOther("some");
30+
DB.save(order);
31+
found.setOrder(order);
32+
DB.save(found);
33+
34+
LoggedSql.start();
35+
MTJTrans found2 = DB.find(MTJTrans.class)
36+
.setId(parent.id())
37+
.fetch("order")
38+
.findOne();
39+
40+
assertThat(found2.order()).isNotNull();
41+
assertThat(found2.order().id()).isEqualTo(order.id());
42+
assertThat(found2.order().other()).isEqualTo("some");
43+
44+
List<String> sql = LoggedSql.stop();
45+
assertThat(sql).hasSize(1);
46+
assertThat(sql.get(0)).contains("from mtjtrans t0 left join mtjorder t1 on t1.org_id = t0.org_id and t1.id = t0.order_id where t0.id = ?");
47+
}
48+
}

0 commit comments

Comments
 (0)