Skip to content

Commit 5db76d3

Browse files
committed
Allow only properties defined within the Entity class; treat properties from non-Entity parent classes as errors.
1 parent 5cd0297 commit 5db76d3

File tree

12 files changed

+129
-50
lines changed

12 files changed

+129
-50
lines changed

src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiPatternUtil.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ object PsiPatternUtil {
157157
targetType: IElementType?,
158158
): MutableList<PsiElement> {
159159
var prevElement = PsiTreeUtil.prevLeaf(element, true)
160-
var prevElements = mutableListOf<PsiElement>()
160+
val prevElements = mutableListOf<PsiElement>()
161161
while (prevElement != null &&
162162
prevElement !is PsiWhiteSpace &&
163163
prevElement.elementType != targetType &&

src/main/kotlin/org/domaframework/doma/intellij/common/util/TypeUtil.kt

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,30 +76,20 @@ object TypeUtil {
7676
type: PsiType?,
7777
project: Project,
7878
): Boolean {
79-
var clazz = type?.let { project.getJavaClazz(type) }
80-
while (clazz != null) {
81-
if (clazz.isEntity()) {
82-
return true
83-
}
84-
clazz = clazz.superClass
85-
}
86-
return false
79+
val clazz = type?.let { project.getJavaClazz(type) }
80+
return clazz?.isEntity() == true
8781
}
8882

8983
fun isImmutableEntity(
9084
project: Project,
9185
canonicalText: String,
9286
): Boolean {
93-
var returnTypeClass = project.getJavaClazz(canonicalText)
94-
while (returnTypeClass != null) {
95-
val entity = returnTypeClass.getClassAnnotation(DomaClassName.ENTITY.className)
96-
if (entity != null) {
97-
return AnnotationUtil.getBooleanAttributeValue(entity, "immutable") == true ||
98-
returnTypeClass.isRecord
99-
}
100-
returnTypeClass = returnTypeClass.superClass
101-
}
102-
return false
87+
val returnTypeClass = project.getJavaClazz(canonicalText)
88+
val entity =
89+
returnTypeClass?.getClassAnnotation(DomaClassName.ENTITY.className) ?: return false
90+
return entity.let { entity ->
91+
AnnotationUtil.getBooleanAttributeValue(entity, "immutable") == true
92+
} || returnTypeClass.isRecord
10393
}
10494

10595
/**

src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/option/DaoAnnotationOptionParameterCheckProcessor.kt

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,8 @@ class DaoAnnotationOptionParameterCheckProcessor(
112112
var searchParamClass: PsiClass? = project.getJavaClazz(searchParamType)
113113

114114
valueFields.forEachIndexed { _, field ->
115-
val currentField =
116-
searchParamClass
117-
?.allFields
118-
?.find { property -> isOptionTargetProperty(property, field, project) }
115+
// Error when specifying a property not defined in the Entity or the Embeddable.
116+
val currentField = getMatchFields(searchParamClass).find { f -> isOptionTargetProperty(f, field, project) }
119117
// Given that the first `searchParamType` is assumed to contain the type of Entity class,
120118
// checking the index for a primitive type is unnecessary.
121119
if (searchParamType is PsiPrimitiveType) {
@@ -168,16 +166,20 @@ class DaoAnnotationOptionParameterCheckProcessor(
168166
.filter { it is PsiLiteralExpression }
169167
}
170168

169+
private fun getMatchFields(paramClass: PsiClass?):List<PsiField> =
170+
paramClass?.allFields?.filter { f ->
171+
val parentClass = f.parent as? PsiClass
172+
(parentClass?.isEntity() == true || parentClass?.isEmbeddable() == true)
173+
&& (TypeUtil.isBaseOrOptionalWrapper(f.type) || TypeUtil.isEmbeddable(f.type, project))
174+
} ?: emptyList()
175+
171176
private fun getTargetOptionProperties(paramClass: PsiClass?) =
172-
paramClass?.allFields?.filter { isOptionTargetProperty(it, it.name, project) }?.joinToString(", ") { it.name.substringAfter(":") }
173-
?: "No fields found"
177+
getMatchFields(paramClass).joinToString(", ") { it.name.substringAfter(":") }
174178

175-
private fun getEmbeddableProperties(embeddableClass: PsiClass?) =
176-
embeddableClass
177-
?.allFields
178-
?.filter { !TypeUtil.isEntity(it.type, project) && !TypeUtil.isEmbeddable(it.type, project) }
179-
?.joinToString(", ") { it.name }
180-
?: "No properties found"
179+
/**
180+
* If the last field access is Embeddable, get its property list
181+
*/
182+
private fun getEmbeddableProperties(embeddableClass: PsiClass?) = getMatchFields(embeddableClass).joinToString(", ") { it.name }
181183

182184
private fun isOptionTargetProperty(
183185
field: PsiField,
@@ -186,7 +188,7 @@ class DaoAnnotationOptionParameterCheckProcessor(
186188
): Boolean =
187189
(
188190
field.name == optionPropertyName && (
189-
!TypeUtil.isEntity(field.type, project) ||
191+
TypeUtil.isBaseOrOptionalWrapper(field.type) ||
190192
TypeUtil.isEmbeddable(field.type, project)
191193
)
192194
)

src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationOptionParameterInspectionTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class AnnotationOptionParameterInspectionTest : DomaSqlTest() {
3333
* - Error check when specifying fields not defined in the parameter type with `exclude` option.
3434
* - Error check for specifying fields not defined in immutable Entity with `MultiInsert` (also for fields not defined in parameter type).
3535
* - Error check for specifying fields not defined in mutable Entity with `MultiInsert`.
36-
* - Error check for specifying fields not defined in parameter type with batch annotations.
36+
* - Error check for specifying fields not defined in the parameter type with batch annotations.
3737
* - Error when ending with an embedded property.
3838
* - Error when specifying incorrect properties in an embedded class.
3939
* - Error check for invalid field specification in `Returning` option.
@@ -42,5 +42,6 @@ class AnnotationOptionParameterInspectionTest : DomaSqlTest() {
4242
* - Error when specifying further properties from a primitive type.
4343
* - Error check for specifying parent class properties in subclass with `@Entity`.
4444
* - Error check for specifying parent class properties in subclass without `@Entity`.
45+
* - Error check for specifying fields from a parent class that is not an Entity.
4546
*/
4647
}

src/test/testData/src/main/java/doma/example/dao/inspection/option/AnnotationOptionTestInValidDao.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package doma.example.dao.inspection.option;
22

33
import doma.example.entity.Department;
4-
import doma.example.entity.NonSubEntity;
4+
import doma.example.entity.DepartmentHasNonEmmbedable;
5+
import doma.example.entity.FacetEntity;
56
import doma.example.entity.Pckt;
6-
import doma.example.entity.SubEntity;
77
import org.seasar.doma.*;
88
import org.seasar.doma.jdbc.MultiResult;
99

@@ -125,4 +125,21 @@ public interface AnnotationOptionTestInValidDao {
125125
*/
126126
@Insert(exclude = "subId.get")
127127
int insertPrimitiveProperty(Department department);
128+
129+
/**
130+
* Case specifying properties defined in an intermediate non-Entity class (Entity subclass)
131+
*
132+
* @param subEntity
133+
* @return
134+
*/
135+
@Insert(exclude = "subName")
136+
int insertReferenceNonEntityProperty2(FacetEntity subEntity);
137+
138+
/**
139+
* Case specifying properties defined in a parent Embeddable class
140+
* Error occurs first on Entity side for non-Embeddable property
141+
* */
142+
@Update(include = "embeddableEntity.childEmbedded3.subAccountNumber")
143+
int updateReferenceParentEmbeddableProperty(DepartmentHasNonEmmbedable department);
144+
128145
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package doma.example.entity;
2+
3+
import doma.example.domain.ClientUser;
4+
import doma.example.domain.ClientUserHasNonEmbeddable;
5+
import org.seasar.doma.Column;
6+
import org.seasar.doma.Entity;
7+
8+
/** Entity class with Embeddable property */
9+
@Entity
10+
public class DepartmentHasNonEmmbedable {
11+
public Integer id;
12+
public String name;
13+
14+
@Column(updatable = false)
15+
public String location;
16+
17+
public Integer managerCount;
18+
19+
int subId;
20+
21+
public ClientUserHasNonEmbeddable embeddableEntity;
22+
23+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package doma.example.entity;
2+
3+
import org.seasar.doma.Entity;
4+
5+
/**
6+
* Entity class at the lowest layer of the inheritance hierarchy
7+
* {@code FoundationEntity}->{@code LayerNonEntity}->{@code FacetNonEntity}
8+
*/
9+
@Entity
10+
public class FacetEntity extends LayerNonEntity {
11+
12+
public Integer firstId;
13+
public String firstName;
14+
public String lastName;
15+
}

src/test/testData/src/main/java/doma/example/entity/ParentEntity.java renamed to src/test/testData/src/main/java/doma/example/entity/FoundationEntity.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
import org.seasar.doma.Entity;
44
import org.seasar.doma.Id;
55

6+
/**
7+
* Entity class at the top of the inheritance hierarchy
8+
* {@code FoundationEntity}
9+
*/
610
@Entity
7-
public class ParentEntity {
11+
public class FoundationEntity {
812

913
@Id
1014
Integer id;

src/test/testData/src/main/java/doma/example/entity/SubEntity.java renamed to src/test/testData/src/main/java/doma/example/entity/LayerEntity.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
import org.seasar.doma.Entity;
44
import org.seasar.doma.Table;
55

6-
/** Class with @Entity on both itself and parent */
6+
/**
7+
* Entity class in the middle of the inheritance hierarchy
8+
* {@code FoundationEntity}->{@code LayerEntity}
9+
*/
710
@Entity
811
@Table(name = "sub_entity_1")
9-
public class SubEntity extends ParentEntity {
12+
public class LayerEntity extends FoundationEntity {
1013
Integer amount;
1114

1215
String subName;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package doma.example.entity;
2+
3+
import org.seasar.doma.Table;
4+
5+
/**
6+
* Non-Entity class in the middle of the inheritance hierarchy
7+
* {@code FoundationEntity}->{@code LayerNonEntity}
8+
* Cannot be used with include/exclude options
9+
* Cannot be specified as a parameter for certain annotation type DAO methods
10+
*/
11+
@Table(name = "sub_entity_2")
12+
public class LayerNonEntity extends FoundationEntity {
13+
14+
Integer amount;
15+
16+
String subName;
17+
}

0 commit comments

Comments
 (0)