Skip to content

Commit 6da2242

Browse files
authored
Fix 157 inconsistent data handling with embedded entities (perplexhub#159)
* Update RSQLJPAContext and RSQLJPAPredicateConverter classes The RSQLJPAContext class has been updated to include a new field 'managedType', while RSQLJPAPredicateConverter class now also returns 'managedType'. The returned context from 'findPropertyPathInternal' method in RSQLJPAPredicateConverter is now being utilized to get the 'managedType'. * Add account-related entities and related tests New model classes for `AccountEntity`, `AddressEntity`, and `AddressHistoryEntity` have been created. The `AccountRepository` interface has also been added. Accompanying these, several tests were introduced into `RSQLJPASupportTest` to ensure correct functionality. * Change access modifiers in RSQLJPAContext The access modifiers for the path, attribute, and managedType variables in the RSQLJPAContext class have been updated. These were previously set as private, but have now been made package-private. * Add Lombok annotations to account entities Replaced manual getters and setters in the account, address, and address history entities with Lombok annotations. This simplifies the code by using the @Getter and @Setter annotations to generate these methods automatically, making the entities more readable and maintainable.
1 parent 249bf00 commit 6da2242

File tree

7 files changed

+209
-5
lines changed

7 files changed

+209
-5
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.github.perplexhub.rsql.model.account;
2+
3+
import jakarta.persistence.CascadeType;
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.Id;
6+
import jakarta.persistence.OneToMany;
7+
import lombok.Getter;
8+
import lombok.Setter;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
@Setter
14+
@Getter
15+
@Entity
16+
public class AccountEntity {
17+
18+
@Id
19+
private String ident;
20+
21+
@OneToMany(cascade = CascadeType.ALL, mappedBy = "account")
22+
private List<AddressHistoryEntity> addressHistory = new ArrayList<>();
23+
24+
public AccountEntity(String ident) {
25+
this.ident = ident;
26+
}
27+
28+
public AccountEntity() {
29+
}
30+
31+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.github.perplexhub.rsql.model.account;
2+
3+
import jakarta.persistence.Embeddable;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
8+
@Setter
9+
@Getter
10+
@Embeddable
11+
public class AddressEntity {
12+
13+
private String name;
14+
private String address;
15+
16+
public AddressEntity(String name, String address) {
17+
this.name = name;
18+
this.address = address;
19+
}
20+
21+
public AddressEntity() {
22+
}
23+
24+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.github.perplexhub.rsql.model.account;
2+
3+
import jakarta.persistence.AttributeOverride;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Embedded;
6+
import jakarta.persistence.Entity;
7+
import jakarta.persistence.GeneratedValue;
8+
import jakarta.persistence.GenerationType;
9+
import jakarta.persistence.Id;
10+
import jakarta.persistence.JoinColumn;
11+
import jakarta.persistence.ManyToOne;
12+
import lombok.Getter;
13+
import lombok.Setter;
14+
15+
import java.time.OffsetDateTime;
16+
17+
@Entity
18+
public class AddressHistoryEntity {
19+
20+
@Setter
21+
@Getter
22+
@Id
23+
@GeneratedValue(strategy = GenerationType.SEQUENCE)
24+
@Column(name = "id", nullable = false)
25+
private Long id;
26+
27+
@Setter
28+
@Getter
29+
@ManyToOne
30+
@JoinColumn(name = "account_ident")
31+
AccountEntity account;
32+
33+
OffsetDateTime activeSince;
34+
35+
@Embedded
36+
@AttributeOverride(name = "name", column = @Column(name = "invoice_name"))
37+
@AttributeOverride(name = "address", column = @Column(name = "invoice_address"))
38+
AddressEntity invoiceAddress;
39+
40+
@Embedded
41+
@AttributeOverride(name = "name", column = @Column(name = "shipping_name"))
42+
@AttributeOverride(name = "address", column = @Column(name = "shipping_address"))
43+
AddressEntity shippingAddress;
44+
45+
public AddressHistoryEntity() {
46+
}
47+
48+
public AddressHistoryEntity(
49+
AccountEntity account,
50+
OffsetDateTime activeSince,
51+
AddressEntity invoiceAddress,
52+
AddressEntity shippingAddress) {
53+
this.account = account;
54+
this.activeSince = activeSince;
55+
this.invoiceAddress = invoiceAddress;
56+
this.shippingAddress = shippingAddress;
57+
}
58+
59+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.github.perplexhub.rsql.repository.jpa;
2+
3+
import io.github.perplexhub.rsql.model.account.AccountEntity;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
6+
7+
8+
public interface AccountRepository extends JpaRepository<AccountEntity, String>, JpaSpecificationExecutor<AccountEntity> {
9+
}

rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAContext.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
import jakarta.persistence.criteria.Path;
44
import jakarta.persistence.metamodel.Attribute;
55

6+
import jakarta.persistence.metamodel.ManagedType;
67
import lombok.Value;
78

89
@Value(staticConstructor = "of")
910
class RSQLJPAContext {
1011

11-
private Path<?> path;
12-
private Attribute<?, ?> attribute;
12+
Path<?> path;
13+
Attribute<?, ?> attribute;
14+
ManagedType<?> managedType;
1315

1416
}

rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ private RSQLJPAContext findPropertyPathInternal(String propertyPath, Path startR
8989
RSQLJPAContext context = findPropertyPathInternal(mappedProperty, root, firstTry);
9090
root = context.getPath();
9191
attribute = context.getAttribute();
92+
classMetadata = context.getManagedType();
9293
} else {
9394
if (!hasPropertyName(mappedProperty, classMetadata)) {
9495
Optional<String> mayBeJSonPath = PathUtils
@@ -165,7 +166,7 @@ private RSQLJPAContext findPropertyPathInternal(String propertyPath, Path startR
165166
accessControl(type, attribute.getName());
166167
}
167168

168-
return RSQLJPAContext.of(root, attribute);
169+
return RSQLJPAContext.of(root, attribute, classMetadata);
169170
}
170171

171172
private String getKeyJoin(Path<?> root, String mappedProperty) {

rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,15 @@
1111

1212
import java.text.ParseException;
1313
import java.text.SimpleDateFormat;
14+
import java.time.OffsetDateTime;
15+
import java.time.ZoneOffset;
1416
import java.util.*;
1517

1618
import io.github.perplexhub.rsql.custom.CustomType;
19+
import io.github.perplexhub.rsql.model.account.AccountEntity;
20+
import io.github.perplexhub.rsql.model.account.AddressEntity;
21+
import io.github.perplexhub.rsql.model.account.AddressHistoryEntity;
22+
import io.github.perplexhub.rsql.repository.jpa.AccountRepository;
1723
import io.github.perplexhub.rsql.repository.jpa.custom.CustomTypeRepository;
1824
import io.github.perplexhub.rsql.custom.EntityWithCustomType;
1925
import jakarta.persistence.criteria.Expression;
@@ -23,10 +29,8 @@
2329
import io.github.perplexhub.rsql.model.Status;
2430
import org.assertj.core.api.Assertions;
2531
import org.assertj.core.groups.Tuple;
26-
import org.junit.Ignore;
2732
import org.junit.jupiter.api.BeforeAll;
2833
import org.junit.jupiter.api.BeforeEach;
29-
import org.junit.jupiter.api.Disabled;
3034
import org.junit.jupiter.api.Test;
3135
import org.junit.jupiter.params.ParameterizedTest;
3236
import org.junit.jupiter.params.provider.ValueSource;
@@ -64,6 +68,9 @@ class RSQLJPASupportTest {
6468
@Autowired
6569
private CustomTypeRepository customTypeRepository;
6670

71+
@Autowired
72+
AccountRepository accountRepository;
73+
6774
@BeforeAll
6875
static void beforeAll() {
6976
RSQLCommonSupport.addConverter(CustomType.class, CustomType::new);
@@ -1362,11 +1369,82 @@ void resetDBBeforeTest() {
13621369
assertEquals(0, size);
13631370
}
13641371

1372+
@Test
1373+
void testSearchForActiveSince() { // this is only a simple query test
1374+
// Given
1375+
AccountEntity account1 = new AccountEntity("account-ident-0");
1376+
account1.setAddressHistory(
1377+
List.of(
1378+
new AddressHistoryEntity(
1379+
account1,
1380+
OffsetDateTime.of(2024, 7, 1, 0, 0, 0, 0, ZoneOffset.UTC),
1381+
new AddressEntity("Name 1", "some address 1"),
1382+
new AddressEntity("Name 1", "some other address 2")),
1383+
new AddressHistoryEntity(
1384+
account1,
1385+
OffsetDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC),
1386+
new AddressEntity("Name 1", "old address 1"),
1387+
new AddressEntity("Name 1", "old address 1"))));
1388+
accountRepository.save(account1);
1389+
// When
1390+
List<AccountEntity> result = accountRepository.findAll(RSQLJPASupport.rsql("addressHistory.activeSince=ge=2024-06-01T00:00:00Z"));
1391+
// Then
1392+
Assertions.assertThat(result).hasSize(1);
1393+
}
1394+
1395+
@Test
1396+
void testSearchInListWhichContainEmbeddedClass() {
1397+
// Given
1398+
AccountEntity account1 = new AccountEntity("account-ident-1");
1399+
account1.setAddressHistory(
1400+
List.of(
1401+
new AddressHistoryEntity(
1402+
account1,
1403+
OffsetDateTime.of(2024, 7, 1, 0, 0, 0, 0, ZoneOffset.UTC),
1404+
new AddressEntity("Name 1", "some address 1"),
1405+
new AddressEntity("Name 1", "some other address 2")),
1406+
new AddressHistoryEntity(
1407+
account1,
1408+
OffsetDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC),
1409+
new AddressEntity("Name 1", "old address 1"),
1410+
new AddressEntity("Name 1", "old address 1"))));
1411+
accountRepository.save(account1);
1412+
// When
1413+
List<AccountEntity> result = accountRepository.findAll(RSQLJPASupport.rsql("addressHistory.invoiceAddress.name=='Name 1'"));
1414+
// Then
1415+
Assertions.assertThat(result).hasSize(1);
1416+
}
1417+
1418+
@Test
1419+
void testSearchWithReducedPathUsingRSQLMapping() {
1420+
// Given
1421+
RSQLCommonSupport.addMapping(AccountEntity.class, "invoiceAddress", "addressHistory.invoiceAddress");
1422+
AccountEntity account1 = new AccountEntity("account-ident-2");
1423+
account1.setAddressHistory(
1424+
List.of(
1425+
new AddressHistoryEntity(
1426+
account1,
1427+
OffsetDateTime.of(2024, 7, 1, 0, 0, 0, 0, ZoneOffset.UTC),
1428+
new AddressEntity("Name 1", "some address 1"),
1429+
new AddressEntity("Name 1", "some other address 2")),
1430+
new AddressHistoryEntity(
1431+
account1,
1432+
OffsetDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC),
1433+
new AddressEntity("Name 1", "old address 1"),
1434+
new AddressEntity("Name 1", "old address 1"))));
1435+
accountRepository.save(account1);
1436+
// When
1437+
List<AccountEntity> result = accountRepository.findAll(RSQLJPASupport.rsql("invoiceAddress.name=='Name 1'"));
1438+
// Then
1439+
Assertions.assertThat(result).hasSize(1);
1440+
}
1441+
13651442
@BeforeEach
13661443
void setUp() {
13671444
getPropertyWhitelist().clear();
13681445
getPropertyBlacklist().clear();
13691446
customTypeRepository.deleteAll();
1447+
accountRepository.deleteAll();
13701448
}
13711449

13721450
}

0 commit comments

Comments
 (0)