Skip to content

Commit 8e71448

Browse files
committed
Update to a more complicated data model with joins
1 parent 65ef830 commit 8e71448

29 files changed

Lines changed: 1411 additions & 439 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.acme.domain;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.Embeddable;
5+
import jakarta.validation.constraints.NotBlank;
6+
7+
@Embeddable
8+
public record Address(
9+
@Column(nullable = false)
10+
@NotBlank(message = "Address is mandatory")
11+
String address,
12+
13+
@Column(nullable = false)
14+
@NotBlank(message = "City is mandatory")
15+
String city,
16+
17+
@Column(nullable = false)
18+
@NotBlank(message = "Country is mandatory")
19+
String country
20+
) {}
Lines changed: 88 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.acme.domain;
22

3+
import java.util.List;
34
import java.util.Objects;
45
import java.util.StringJoiner;
56

@@ -8,79 +9,97 @@
89
import jakarta.persistence.GeneratedValue;
910
import jakarta.persistence.GenerationType;
1011
import jakarta.persistence.Id;
12+
import jakarta.persistence.OneToMany;
13+
import jakarta.persistence.SequenceGenerator;
1114
import jakarta.persistence.Table;
1215
import jakarta.validation.constraints.NotBlank;
1316

17+
import org.hibernate.annotations.NaturalId;
18+
1419
@Entity
1520
@Table(name = "fruits")
1621
public class Fruit {
17-
@Id
18-
@GeneratedValue(strategy = GenerationType.IDENTITY)
19-
private Long id;
20-
21-
@Column(nullable = false, unique = true)
22-
@NotBlank(message = "Name is mandatory")
23-
private String name;
24-
private String description;
25-
26-
public Fruit() {
27-
}
28-
29-
public Fruit(Long id, String name, String description) {
30-
this.id = id;
31-
this.name = name;
32-
this.description = description;
33-
}
34-
35-
public Long getId() {
36-
return this.id;
37-
}
38-
39-
public void setId(Long id) {
40-
this.id = id;
41-
}
42-
43-
public String getName() {
44-
return this.name;
45-
}
46-
47-
public void setName(String name) {
48-
this.name = name;
49-
}
50-
51-
public String getDescription() {
52-
return this.description;
53-
}
54-
55-
public void setDescription(String description) {
56-
this.description = description;
57-
}
58-
59-
@Override
60-
public String toString() {
61-
return new StringJoiner(", ", Fruit.class.getSimpleName() + "[", "]")
62-
.add("id=" + this.id)
63-
.add("name='" + this.name + "'")
64-
.add("description='" + this.description + "'")
65-
.toString();
66-
}
67-
68-
@Override
69-
public boolean equals(Object o) {
70-
if (this == o) {
71-
return true;
72-
}
73-
74-
if ((o == null) || (getClass() != o.getClass())) {
75-
return false;
76-
}
77-
78-
Fruit fruit = (Fruit) o;
79-
return this.id.equals(fruit.id);
80-
}
81-
82-
@Override
83-
public int hashCode() {
84-
return Objects.hash(this.id);
85-
}
22+
@Id
23+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "fruits_seq")
24+
@SequenceGenerator(name = "fruits_seq", sequenceName = "fruits_seq", allocationSize = 1)
25+
private Long id;
26+
27+
@Column(nullable = false, unique = true)
28+
@NaturalId
29+
@NotBlank(message = "Name is mandatory")
30+
private String name;
31+
private String description;
32+
33+
@OneToMany(mappedBy = "fruit")
34+
private List<StoreFruitPrice> storePrices;
35+
36+
public Fruit() {
37+
}
38+
39+
public Fruit(Long id, String name, String description) {
40+
this.id = id;
41+
this.name = name;
42+
this.description = description;
43+
}
44+
45+
public Long getId() {
46+
return this.id;
47+
}
48+
49+
public void setId(Long id) {
50+
this.id = id;
51+
}
52+
53+
public String getName() {
54+
return this.name;
55+
}
56+
57+
public void setName(String name) {
58+
this.name = name;
59+
}
60+
61+
public String getDescription() {
62+
return this.description;
63+
}
64+
65+
public void setDescription(String description) {
66+
this.description = description;
67+
}
68+
69+
public List<StoreFruitPrice> getStorePrices() {
70+
return storePrices;
71+
}
72+
73+
public void setStorePrices(List<StoreFruitPrice> storePrices) {
74+
this.storePrices = storePrices;
75+
}
76+
77+
@Override
78+
public String toString() {
79+
return new StringJoiner(", ", Fruit.class.getSimpleName() + "[", "]")
80+
.add("id=" + this.id)
81+
.add("name='" + this.name + "'")
82+
.add("description='" + this.description + "'")
83+
.add("storePrices=" + this.storePrices)
84+
.toString();
85+
}
86+
87+
@Override
88+
public boolean equals(Object o) {
89+
if (this == o) {
90+
return true;
91+
}
92+
93+
if ((o == null) || (getClass() != o.getClass())) {
94+
return false;
95+
}
96+
97+
Fruit fruit = (Fruit) o;
98+
return this.id.equals(fruit.id);
99+
}
100+
101+
@Override
102+
public int hashCode() {
103+
return Objects.hash(this.id);
104+
}
86105
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package org.acme.domain;
2+
3+
import java.util.Objects;
4+
import java.util.StringJoiner;
5+
6+
import jakarta.persistence.Column;
7+
import jakarta.persistence.Embedded;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.GeneratedValue;
10+
import jakarta.persistence.GenerationType;
11+
import jakarta.persistence.Id;
12+
import jakarta.persistence.SequenceGenerator;
13+
import jakarta.persistence.Table;
14+
import jakarta.validation.constraints.NotBlank;
15+
16+
import org.hibernate.annotations.NaturalId;
17+
18+
@Entity
19+
@Table(name = "stores")
20+
public class Store {
21+
@Id
22+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "stores_seq")
23+
@SequenceGenerator(name = "stores_seq", sequenceName = "stores_seq", allocationSize = 1)
24+
private Long id;
25+
26+
@Column(nullable = false, unique = true)
27+
@NaturalId
28+
@NotBlank(message = "Name is mandatory")
29+
private String name;
30+
31+
@Column(nullable = false)
32+
@NotBlank(message = "Currency is mandatory")
33+
private String currency;
34+
35+
@Embedded
36+
private Address address;
37+
38+
public Store() {}
39+
40+
public Store(Long id, String name, Address address, String currency) {
41+
this.id = id;
42+
this.name = name;
43+
this.address = address;
44+
this.currency = currency;
45+
}
46+
47+
public Long getId() { return id; }
48+
public void setId(Long id) { this.id = id; }
49+
50+
public String getName() { return name; }
51+
public void setName(String name) { this.name = name; }
52+
53+
public Address getAddress() { return address; }
54+
public void setAddress(Address address) { this.address = address; }
55+
56+
public String getCurrency() {
57+
return currency;
58+
}
59+
60+
public void setCurrency(String currency) {
61+
this.currency = currency;
62+
}
63+
64+
@Override
65+
public String toString() {
66+
return new StringJoiner(", ", Store.class.getSimpleName() + "[", "]")
67+
.add("id=" + id)
68+
.add("name='" + name + "'")
69+
.add("address=" + address)
70+
.add("currency='" + currency + "'")
71+
.toString();
72+
}
73+
74+
@Override
75+
public boolean equals(Object o) {
76+
if (this == o) return true;
77+
if (o == null || getClass() != o.getClass()) return false;
78+
Store store = (Store) o;
79+
return Objects.equals(id, store.id);
80+
}
81+
82+
@Override
83+
public int hashCode() {
84+
return Objects.hash(id);
85+
}
86+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package org.acme.domain;
2+
3+
import java.math.BigDecimal;
4+
import java.util.Objects;
5+
6+
import jakarta.persistence.Column;
7+
import jakarta.persistence.EmbeddedId;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.FetchType;
10+
import jakarta.persistence.JoinColumn;
11+
import jakarta.persistence.ManyToOne;
12+
import jakarta.persistence.MapsId;
13+
import jakarta.persistence.Table;
14+
import jakarta.validation.constraints.DecimalMin;
15+
import jakarta.validation.constraints.Digits;
16+
import jakarta.validation.constraints.NotNull;
17+
18+
import com.fasterxml.jackson.annotation.JsonIgnore;
19+
20+
@Entity
21+
@Table(name = "store_fruit_prices")
22+
public class StoreFruitPrice {
23+
@EmbeddedId
24+
@JsonIgnore
25+
private StoreFruitPriceId id;
26+
27+
@MapsId("storeId")
28+
@ManyToOne(fetch = FetchType.LAZY, optional = false)
29+
@JoinColumn(name = "store_id", nullable = false)
30+
private Store store;
31+
32+
@MapsId("fruitId")
33+
@ManyToOne(fetch = FetchType.LAZY, optional = false)
34+
@JoinColumn(name = "fruit_id", nullable = false)
35+
@JsonIgnore
36+
private Fruit fruit;
37+
38+
@NotNull
39+
@DecimalMin(value = "0.00", message = "Price must be >= 0")
40+
@Digits(integer = 10, fraction = 2)
41+
@Column(nullable = false, precision = 12, scale = 2)
42+
private BigDecimal price;
43+
44+
public StoreFruitPrice() {}
45+
46+
public StoreFruitPrice(Store store, Fruit fruit, BigDecimal price) {
47+
this.store = store;
48+
this.fruit = fruit;
49+
this.price = price;
50+
this.id = new StoreFruitPriceId(store, fruit);
51+
}
52+
53+
public StoreFruitPriceId getId() { return id; }
54+
public void setId(StoreFruitPriceId id) { this.id = id; }
55+
56+
public Store getStore() { return store; }
57+
public void setStore(Store store) {
58+
this.store = store;
59+
this.id = new StoreFruitPriceId((store != null) ? store.getId() : null,
60+
(this.id != null) ? this.id.fruitId() : null);
61+
}
62+
63+
public Fruit getFruit() { return fruit; }
64+
public void setFruit(Fruit fruit) {
65+
this.fruit = fruit;
66+
this.id = new StoreFruitPriceId((this.id != null) ? this.id.storeId() : null,
67+
(fruit != null) ? fruit.getId() : null);
68+
}
69+
70+
public BigDecimal getPrice() { return price; }
71+
public void setPrice(BigDecimal price) { this.price = price; }
72+
73+
@Override
74+
public boolean equals(Object o) {
75+
if (!(o instanceof StoreFruitPrice that)) return false;
76+
return Objects.equals(id, that.id);
77+
}
78+
79+
@Override
80+
public int hashCode() {
81+
return Objects.hashCode(id);
82+
}
83+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.acme.domain;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.Embeddable;
5+
6+
@Embeddable
7+
public record StoreFruitPriceId(
8+
@Column(nullable = false) Long storeId,
9+
@Column(nullable = false) Long fruitId
10+
) {
11+
12+
public StoreFruitPriceId(Store store, Fruit fruit) {
13+
this((store != null) ? store.getId() : null, (fruit != null) ? fruit.getId() : null);
14+
}
15+
16+
// JPA needs a no-arg constructor; records don't have it, but most providers support record components.
17+
// If your JPA provider requires, keep a synthetic no-arg constructor:
18+
// public StoreFruitPriceId() {
19+
// this(null, null);
20+
// }
21+
}

0 commit comments

Comments
 (0)