问题
I'm using Spring framework. I have two tables Package & Item and the relationship is one Package has many Items. The tables design is something like:
Packages
@Entity
@Table(name = "TB_PACKAGE")
public class Packages implements Serializable{
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(name = "PACKAGE_ID")
private String packageId;
@Valid
@OneToMany(mappedBy = "packages", cascade = { CascadeType.ALL })
private List<Item> item;
Item
@Entity
@Table(name = "TB_ITEM")
public class Item implements Serializable{
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(name = "ITEM_ID")
private String itemId;
@ManyToOne
@JoinColumn(name="PACKAGE_ID")
private Packages packages;
@Transient
private String packageId;
Sample records
This is how I write my code to join table by using CriteriaBuilder to get result like this.
SQL:
SELECT * FROM Packages p JOIN Item i ON p.packageId = i.packageId;
Java:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Packages> cq = cb.createQuery(Packages.class);
Root<Packages> pRoot = cq.from(Packages.class);
Join<Packages, Item> packagesItem = pRoot.join("item");
List<Predicate> predicates = new ArrayList<Predicate>();
if (StringUtils.isNotEmpty(itemName)) {
predicates.add(cb.like(cb.lower(packagesItem.<String>get("itemName")), "%" + itemName.toLowerCase() + "%"));
}
Predicate[] predArray = new Predicate[predicates.size()];
predicates.toArray(predArray);
cq.where(predArray);
cq.distinct(true);
TypedQuery<Packages> query = em.createQuery(cq);
return query.getResultList();
If I enter 'phone' as my parameter, it suppose should come out with a record as JSON:
"packageId": "P1",
"item": [
{
"itemId": "Item 1",
"temName" "Phone"
}
]
However the packages always load with all its child like this:
"packageId": "P1",
"item": [
{
"itemId": "Item 1",
"temName" "Phone"
},
{
"itemId": "Item 2",
"temName" "Laptop"
},
{
"itemId": "Item 3",
"temName" "Mouse"
}
]
I have tried everything: FetchType.Lazy, QueryDsl, Native Query, HQL and etc. but still no luck. Anyone has the solution?
回答1:
I think the problem is on the SQL syntax, on JOIN
.
INNER JOIN: Returns all rows when there is at least one match in BOTH tables
LEFT JOIN: Return all rows from the left table, and the matched rows from the right table
sometimes it's called
LEFT OUTER JOIN
.RIGHT JOIN: Return all rows from the right table, and the matched rows from the left table
FULL JOIN: Return all rows when there is a match in ONE of the tables
When you use JOIN
, by default, at least for SQL Server and MySQL, is equivalant to INNER JOIN
. I think your underlying lib/jar will also provide you with classes for other JOIN
types.
Try specify INNER JOIN
, or give WHERE
a try. For me WHERE
is more clear.
This question may help:
Hibernate default joining for nullable many-to-one
回答2:
Your @OneToMany
mapping doesn't seem to be right.
Try changing
@Valid
@OneToMany(mappedBy = "items", cascade = { CascadeType.ALL })
private List<Item> item;
to
@Valid
@OneToMany(mappedBy = "packages", cascade = { CascadeType.ALL })
private List<Item> item;
Once you are done with your mappings you will have to check JOIN FETCH
回答3:
What you need is JOIN FETCH instead of JOIN
Join<Packages, Item> packagesItem = pRoot.fetch("item");
as a JPQL
Select packages from Packages packages join fetch packages.item where packages.item.temName like '%'+itemName+'%';
how-to-properly-express-jpql-join-fetch-with-where-clause-as-jpa-2-criteriaq
来源:https://stackoverflow.com/questions/40626002/jpa-one-to-many-join-table-give-incorrect-child-records