Skip to content

Commit 2e6a86e

Browse files
authored
Merge pull request #444 from domaframework/association-for-immutable-entities
Support to associate immutable entities
2 parents e388ab9 + eb31c35 commit 2e6a86e

File tree

8 files changed

+335
-17
lines changed

8 files changed

+335
-17
lines changed

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/command/AssociateCommand.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import java.util.List;
77
import java.util.Map;
88
import java.util.Objects;
9-
import java.util.function.BiConsumer;
9+
import java.util.function.BiFunction;
1010
import org.seasar.doma.internal.util.Pair;
1111
import org.seasar.doma.jdbc.command.Command;
1212
import org.seasar.doma.jdbc.command.SelectCommand;
@@ -37,7 +37,7 @@ public List<ENTITY> execute() {
3737
query, new EntityPoolIterationHandler(context.getProjectionEntityMetamodels()));
3838
List<EntityPool> entityPools = command.execute();
3939
for (EntityPool entityPool : entityPools) {
40-
Map<EntityMetamodel<?>, Object> associationCandidate = new LinkedHashMap<>();
40+
Map<EntityMetamodel<?>, Pair<EntityKey, Object>> associationCandidate = new LinkedHashMap<>();
4141
for (Map.Entry<EntityKey, EntityData> e : entityPool.entrySet()) {
4242
EntityKey key = e.getKey();
4343
EntityData data = e.getValue();
@@ -53,9 +53,9 @@ public List<ENTITY> execute() {
5353
}
5454
return newEntity;
5555
});
56-
associationCandidate.put(key.getEntityMetamodel(), entity);
56+
associationCandidate.put(key.getEntityMetamodel(), new Pair<>(key, entity));
5757
}
58-
associate(associationCandidate);
58+
associate(cache, associationCandidate);
5959
}
6060
return (List<ENTITY>)
6161
cache.entrySet().stream()
@@ -64,17 +64,23 @@ public List<ENTITY> execute() {
6464
.collect(toList());
6565
}
6666

67-
private void associate(Map<EntityMetamodel<?>, Object> associationCandidate) {
68-
for (Map.Entry<Pair<EntityMetamodel<?>, EntityMetamodel<?>>, BiConsumer<Object, Object>> e :
69-
context.associations.entrySet()) {
70-
Pair<EntityMetamodel<?>, EntityMetamodel<?>> pair = e.getKey();
71-
BiConsumer<Object, Object> associator = e.getValue();
72-
Object entity1 = associationCandidate.get(pair.fst);
73-
Object entity2 = associationCandidate.get(pair.snd);
74-
if (entity1 == null || entity2 == null) {
67+
private void associate(
68+
Map<EntityKey, Object> cache,
69+
Map<EntityMetamodel<?>, Pair<EntityKey, Object>> associationCandidate) {
70+
for (Map.Entry<Pair<EntityMetamodel<?>, EntityMetamodel<?>>, BiFunction<Object, Object, Object>>
71+
e : context.associations.entrySet()) {
72+
Pair<EntityMetamodel<?>, EntityMetamodel<?>> metamodelPair = e.getKey();
73+
BiFunction<Object, Object, Object> associator = e.getValue();
74+
Pair<EntityKey, Object> keyAndEntity1 = associationCandidate.get(metamodelPair.fst);
75+
Pair<EntityKey, Object> keyAndEntity2 = associationCandidate.get(metamodelPair.snd);
76+
if (keyAndEntity1 == null || keyAndEntity2 == null) {
7577
continue;
7678
}
77-
associator.accept(entity1, entity2);
79+
Object newEntity = associator.apply(keyAndEntity1.snd, keyAndEntity2.snd);
80+
if (newEntity != null) {
81+
cache.replace(keyAndEntity1.fst, newEntity);
82+
associationCandidate.replace(metamodelPair.fst, new Pair<>(keyAndEntity1.fst, newEntity));
83+
}
7884
}
7985
}
8086

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/context/SelectContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.util.List;
99
import java.util.Map;
1010
import java.util.Objects;
11-
import java.util.function.BiConsumer;
11+
import java.util.function.BiFunction;
1212
import java.util.stream.Stream;
1313
import org.seasar.doma.internal.util.Pair;
1414
import org.seasar.doma.jdbc.criteria.metamodel.EntityMetamodel;
@@ -28,7 +28,7 @@ public class SelectContext implements Context {
2828
public Integer limit;
2929
public Integer offset;
3030
public ForUpdate forUpdate = new ForUpdate(ForUpdateOption.none());
31-
public final Map<Pair<EntityMetamodel<?>, EntityMetamodel<?>>, BiConsumer<Object, Object>>
31+
public final Map<Pair<EntityMetamodel<?>, EntityMetamodel<?>>, BiFunction<Object, Object, Object>>
3232
associations = new LinkedHashMap<>();
3333
public final SelectSettings settings = new SelectSettings();
3434

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/declaration/SelectFromDeclaration.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.Objects;
88
import java.util.Set;
99
import java.util.function.BiConsumer;
10+
import java.util.function.BiFunction;
1011
import java.util.function.Consumer;
1112
import org.seasar.doma.DomaException;
1213
import org.seasar.doma.internal.util.Pair;
@@ -164,7 +165,38 @@ public <ENTITY1, ENTITY2> void associate(
164165
}
165166
return;
166167
}
167-
//noinspection unchecked
168-
context.associations.put(new Pair<>(first, second), (BiConsumer<Object, Object>) associator);
168+
context.associations.put(
169+
new Pair<>(first, second),
170+
(entity1, entity2) -> {
171+
associator.accept((ENTITY1) entity1, (ENTITY2) entity2);
172+
return null;
173+
});
174+
}
175+
176+
@SuppressWarnings("unchecked")
177+
public <ENTITY1, ENTITY2> void associateWith(
178+
EntityMetamodel<ENTITY1> first,
179+
EntityMetamodel<ENTITY2> second,
180+
BiFunction<ENTITY1, ENTITY2, ENTITY1> associator,
181+
AssociationOption option) {
182+
Objects.requireNonNull(first);
183+
Objects.requireNonNull(second);
184+
Objects.requireNonNull(associator);
185+
Objects.requireNonNull(option);
186+
if (!context.getEntityMetamodels().contains(first)) {
187+
if (option == AssociationOption.Kind.MANDATORY) {
188+
throw new DomaException(Message.DOMA6010, "first");
189+
}
190+
return;
191+
}
192+
if (!context.getEntityMetamodels().contains(second)) {
193+
if (option == AssociationOption.Kind.MANDATORY) {
194+
throw new DomaException(Message.DOMA6010, "second");
195+
}
196+
return;
197+
}
198+
context.associations.put(
199+
new Pair<>(first, second),
200+
(entity1, entity2) -> associator.apply((ENTITY1) entity1, (ENTITY2) entity2));
169201
}
170202
}

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/statement/EntityqlSelectStarting.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.List;
55
import java.util.Objects;
66
import java.util.function.BiConsumer;
7+
import java.util.function.BiFunction;
78
import java.util.function.Consumer;
89
import org.seasar.doma.jdbc.Config;
910
import org.seasar.doma.jdbc.command.Command;
@@ -82,6 +83,30 @@ public <ENTITY1, ENTITY2> EntityqlSelectStarting<ENTITY> associate(
8283
return this;
8384
}
8485

86+
public <ENTITY1, ENTITY2> EntityqlSelectStarting<ENTITY> associateWith(
87+
EntityMetamodel<ENTITY1> first,
88+
EntityMetamodel<ENTITY2> second,
89+
BiFunction<ENTITY1, ENTITY2, ENTITY1> associator) {
90+
Objects.requireNonNull(first);
91+
Objects.requireNonNull(second);
92+
Objects.requireNonNull(associator);
93+
declaration.associateWith(first, second, associator, AssociationOption.mandatory());
94+
return this;
95+
}
96+
97+
public <ENTITY1, ENTITY2> EntityqlSelectStarting<ENTITY> associateWith(
98+
EntityMetamodel<ENTITY1> first,
99+
EntityMetamodel<ENTITY2> second,
100+
BiFunction<ENTITY1, ENTITY2, ENTITY1> associator,
101+
AssociationOption option) {
102+
Objects.requireNonNull(first);
103+
Objects.requireNonNull(second);
104+
Objects.requireNonNull(associator);
105+
Objects.requireNonNull(option);
106+
declaration.associateWith(first, second, associator, option);
107+
return this;
108+
}
109+
85110
public EntityqlSelectStarting<ENTITY> where(Consumer<WhereDeclaration> block) {
86111
Objects.requireNonNull(block);
87112
declaration.where(block);

doma-core/src/main/java/org/seasar/doma/message/Message.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,10 @@ public enum Message implements MessageResource {
949949
DOMA6009(
950950
"The parameter \"{0}\" is unknown. "
951951
+ "Ensure that you have passed it to the from, the innerJoin, or the leftJoin method before invoking the select method."),
952+
DOMA6010(
953+
"The parameter \"{0}\" is unknown. "
954+
+ "Ensure that you have passed it to the from, the innerJoin, or the leftJoin method before invoking the associateWith method. "
955+
+ "If the innerJoin or leftJoin method call is optional, pass the AssociationKind.OPTIONAL value to the associateWith method."),
952956
;
953957

954958
private final String messagePattern;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package example;
2+
3+
import org.seasar.doma.Entity;
4+
import org.seasar.doma.Id;
5+
import org.seasar.doma.Metamodel;
6+
import org.seasar.doma.Table;
7+
import org.seasar.doma.Version;
8+
9+
@Entity(immutable = true, metamodel = @Metamodel)
10+
@Table(name = "DEPARTMENT")
11+
public class Dept {
12+
13+
@Id private final Integer departmentId;
14+
private final Integer departmentNo;
15+
private final String departmentName;
16+
private final String location;
17+
@Version private final Integer version;
18+
19+
public Dept(
20+
Integer departmentId,
21+
Integer departmentNo,
22+
String departmentName,
23+
String location,
24+
Integer version) {
25+
this.departmentId = departmentId;
26+
this.departmentNo = departmentNo;
27+
this.departmentName = departmentName;
28+
this.location = location;
29+
this.version = version;
30+
}
31+
32+
public Integer getDepartmentId() {
33+
return departmentId;
34+
}
35+
36+
public Integer getDepartmentNo() {
37+
return departmentNo;
38+
}
39+
40+
public String getDepartmentName() {
41+
return departmentName;
42+
}
43+
44+
public String getLocation() {
45+
return location;
46+
}
47+
48+
public Integer getVersion() {
49+
return version;
50+
}
51+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package example;
2+
3+
import java.time.LocalDate;
4+
import org.seasar.doma.Entity;
5+
import org.seasar.doma.Id;
6+
import org.seasar.doma.Metamodel;
7+
import org.seasar.doma.Table;
8+
import org.seasar.doma.Transient;
9+
import org.seasar.doma.Version;
10+
11+
@Entity(immutable = true, metamodel = @Metamodel)
12+
@Table(name = "EMPLOYEE")
13+
public class Emp {
14+
15+
@Id private final Integer employeeId;
16+
private final Integer employeeNo;
17+
private final String employeeName;
18+
private final Integer managerId;
19+
private final LocalDate hiredate;
20+
private final Salary salary;
21+
private final Integer departmentId;
22+
private final Integer addressId;
23+
@Version private final Integer version;
24+
@Transient private final Dept department;
25+
@Transient private final Emp manager;
26+
27+
public Emp(
28+
Integer employeeId,
29+
Integer employeeNo,
30+
String employeeName,
31+
Integer managerId,
32+
LocalDate hiredate,
33+
Salary salary,
34+
Integer departmentId,
35+
Integer addressId,
36+
Integer version) {
37+
this(
38+
employeeId,
39+
employeeNo,
40+
employeeName,
41+
managerId,
42+
hiredate,
43+
salary,
44+
departmentId,
45+
addressId,
46+
version,
47+
null,
48+
null);
49+
}
50+
51+
public Emp(
52+
Integer employeeId,
53+
Integer employeeNo,
54+
String employeeName,
55+
Integer managerId,
56+
LocalDate hiredate,
57+
Salary salary,
58+
Integer departmentId,
59+
Integer addressId,
60+
Integer version,
61+
Dept department,
62+
Emp manager) {
63+
this.employeeId = employeeId;
64+
this.employeeNo = employeeNo;
65+
this.employeeName = employeeName;
66+
this.managerId = managerId;
67+
this.hiredate = hiredate;
68+
this.salary = salary;
69+
this.departmentId = departmentId;
70+
this.addressId = addressId;
71+
this.version = version;
72+
this.department = department;
73+
this.manager = manager;
74+
}
75+
76+
public Emp withDept(Dept dept) {
77+
return new Emp(
78+
this.getEmployeeId(),
79+
this.getEmployeeNo(),
80+
this.getEmployeeName(),
81+
this.getManagerId(),
82+
this.getHiredate(),
83+
this.getSalary(),
84+
this.getDepartmentId(),
85+
this.getAddressId(),
86+
this.getVersion(),
87+
dept,
88+
this.getManager());
89+
}
90+
91+
public Emp withManager(Emp manager) {
92+
return new Emp(
93+
this.getEmployeeId(),
94+
this.getEmployeeNo(),
95+
this.getEmployeeName(),
96+
this.getManagerId(),
97+
this.getHiredate(),
98+
this.getSalary(),
99+
this.getDepartmentId(),
100+
this.getAddressId(),
101+
this.getVersion(),
102+
this.getDepartment(),
103+
manager);
104+
}
105+
106+
public Integer getEmployeeId() {
107+
return employeeId;
108+
}
109+
110+
public Integer getEmployeeNo() {
111+
return employeeNo;
112+
}
113+
114+
public String getEmployeeName() {
115+
return employeeName;
116+
}
117+
118+
public Integer getManagerId() {
119+
return managerId;
120+
}
121+
122+
public LocalDate getHiredate() {
123+
return hiredate;
124+
}
125+
126+
public Salary getSalary() {
127+
return salary;
128+
}
129+
130+
public Integer getDepartmentId() {
131+
return departmentId;
132+
}
133+
134+
public Integer getAddressId() {
135+
return addressId;
136+
}
137+
138+
public Integer getVersion() {
139+
return version;
140+
}
141+
142+
public Dept getDepartment() {
143+
return department;
144+
}
145+
146+
public Emp getManager() {
147+
return manager;
148+
}
149+
}

0 commit comments

Comments
 (0)