org.springframework.dao.InvalidDataAccessApiUsageException:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously
fetch multiple bags: [Order.items, OrderItem.options];
Above is an exception i faced when i join three tables like below.
OrderItemOption.java
#Entity
public class OrderItemOption {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_option_id")
private Long id;
#Column(name = "item_id", nullable = false)
private Long itemId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "item_id",
referencedColumnName = "item_id",
insertable = false,
updatable = false
)
private OrderItem orderItem;
}
OrderItem.java
#Entity
public class OrderItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private Long id;
#Column(name = "order_id", nullable = false)
private Long orderId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "order_id",
referencedColumnName = "order_id",
insertable = false,
updatable = false,
nullable = false
)
private Order order;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "orderItem")
#OrderBy("item_option_id ASC")
private List<OrderItemOption> options;
}
Order.java
#Entity
public class Order {
#Id
#Column(name = "order_id", nullable = false)
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
#OrderBy("item_id ASC")
private List<OrderItem> items;
}
And here's my QueryDSL code to join them at one time.
final QOrder order = QOrder.order;
final QOrderItem item = QOrderItem.orderItem;
final QOrderItemOption option = QOrderItemOption.orderItemOption;
from(order)
.leftJoin(order.items, item).fetchJoin()
.leftJoin(item.options, option).fetchJoin()
.where(
order.id.eq(orderId)
.and(item.id.in(itemIds))
.and(option.id.in(optionIds))
)
.fetchOne())
What i'm trying to do is to get Order object which contains filtered relationship, so that i can access filtered children via order object.
and the type of relationship should be a List, not a Set.
for example, order.getItems().get(0).getOptions.get(0)
How can i achieve that goal?
To avoid above exception there are two possibilities:
Change List to Set
or
Use List but do not fetch two bags. This means don't use fetchJoin() on both collections.
Filtering:
Using where conditions collections will be not filtered. Collections will contain all associated objects. Joining in JPA is for creating conditions on root object - Order. It is not the same as in SQL.
It is possible to filter associated collections using JPA 2.1 JOIN ON feature. This allows additional conditions in ON clause
see for example QueryDSL Left Join with additional conditions in ON
If you really can not use Set instead of List:
Parent.class
#OneToMany(
mappedBy = "parent",
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.MERGE }
)
#OrderColumn(name = "position")
private List<Child> childs = new ArrayList<>();
Child.class
#ManyToOne(fetch = FetchType.LAZY)
private Parent parent;
And create a column in the Child's table named e.g "position"
ALTER TABLE child ADD COLUMN position integer NOT NULL default 0
And if you can not use other column in table, them you need to query the lists in sequence. Or use the id of the child and a custom getter.
#OrderColumn(name = "id_child", updatable = false, insertable = false)
public List<Child> getChilds() {
childs.removeAll(Collections.singleton(null));
return childs;
}
Related
There is a SecondaryClass, which has embedded id that consists of NOT ids (not PKs) but simple string columns of two tables - Foo and Bar.
#Embeddable
public class SecondaryClassId implements Serializable {
private String fooPublicId;
private Long barPublicId;
}
I want this composite key to be unique for each PrimaryClass. PrimaryClass is an uber-class, which has one-to-many mapping with SecondaryClass. There may be many combinations where different PrimaryClasses will be created with already existing in database Foo's and Bar's. I want to say to Hibernate "ok so if there already is a Foo row with id jsd72bd or Bar row with id906hd just relate to those ones, don't create new duplicated row with the same ids".
#Entity
public class SecondaryClass implements Serializable {
#EmbeddedId
private SecondaryClassId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "bar_id", referencedColumnName = "bar_public_id", insertable = false, updatable = false)
private Foo foo;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "foo_id", referencedColumnName = "foo_public_id", insertable = false, updatable = false)
private Bar bar;
#ManyToOne
#JoinColumn(name = "primary_id", referencedColumnName = "id", insertable = false, updatable = false)
private PrimaryClass primaryClass;
}
I keep getting an error:
EntityExistsException A different object with the same identifier value was already associated with the session
#Entity
public class PrimaryClass implements Serializable {
#Id
#Column(name = "id")
private Long id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "primaryClass")
private Set<SecondaryClass> secondaries = new HashSet<>();
}
I have two tables Company and Employees, one-to-many mapping. Company table contains composite primary key.
I want to search from company table based on primary id but want to put an additional check on the child table.
I want to load only a particular type of employees which I will get in the request. How it can be done in Sprongboot JPA with findById("id");
class Company{
#Id
private String companyId;
#Id
private String stateId;
private String company Name;
#OneToMany(targetEntity = Employees.class, fetch = FetchType.LAZY, cascade = {
CascadeType.ALL }, mappedBy = "company")
private Set<Employees> empList;
}
class Employees{
#Id
private String id;
//foreign key
private String companyId;
//foreign key
private String stateId;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "companyId", referencedColumnName = "companyId", insertable = false, updatable = false, nullable = true),
#JoinColumn(name = "stateId", referencedColumnName = "stateId", insertable = false, updatable = false, nullable = true) })
private Company company;
private int salary;
private String type;
}
Use Filter, which is an alternative of #Where where you can set dynamic value.
Here is the sample
#FilterDef(
name = "employeeTypeFilter",
parameters = #ParamDef(name = "type", type = "string")
)
#Filter(
name = "employeeTypeFilter",
condition = "type > : type"
)
public class Employees {
}
You can enable or disable filter from your code dynamically based on your requirement.
You can use #Where for fixed type
#Where(clause = "type = 'anyEmployeeType'")
private Set<Employees> empList;
For dynamically fetch you can query in Employees repository
List<Employees> findByTypeAndCompany(String type, Company company);
I'm looking for a way to implement (0..n) many to many relation in JPA, much possibly using #ManyToMany annotation. All examples that I found were about (1..n) relations. What I need to accomplish:
- I've got two entities: Contact and Tag. Each Contact can have 0..n Tags. Each Tag can have 0..n Contacts. From SQL point of view it would look like
this: Contact (0..n) --- (1) Contact_has_Tag (1) --- (0..n) Tag.
Code below is not working for me because JPA is linking columns with INNER JOIN.
OFC I could do this using intermediate entity and #OneToMany and #ManyToOne annotations, but I want a simpler sollution.
#Data
#Entity
public class Contact {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private long id;
#Column(unique = true)
private String email;
// ...
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonIgnore
#JoinTable(
name = "contact_has_tag",
joinColumns = #JoinColumn(name = "contact_id", referencedColumnName = "id", updatable = false, nullable = true),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "id", updatable = false, nullable = true))
private List<ContactTag> contactTags = new ArrayList<ContactTag>();
}
#Entity
#Data
public class ContactTag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column
private String name;
#ManyToMany(mappedBy="contactTags", fetch = FetchType.LAZY)
#JsonIgnore
private List<Contact> contacts = new ArrayList<Contact>();
}
Any ideas how it should be done?
I have the following model that I need to annotate using JPA:
Merchant(merchant_id, ...).
MerchantType(id1, id2, ...)
MerchantMerchantTypeAssociationTable(merchant_id, id1, id2)
I cannot figure out how to map the association table. Mapping Merchant is straitghtforward, so I will leave it outside of the mappings. The other mappings are as follows:
MerchantType:
#Entity
class MerchantType {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "e1_id", column=#Column(name="e1_id")),
#AttributeOverride(name = "another_id", column=#Column(name="another_id"))
})
MerchantTypePk id;
#ManyToOne
#JoinColumn(name = "e1_id", referencedColumnName = "e1_id", insertable = false, nullable = false)
#MapsId("e1_id")
AnotherEntity1 e1;
#Column(name = "another_id", referencedColumnName = "another_id", insertable = false, nullable = false)
Long anotherId;
//Two other local fields irrelevant to the discussion here
public MerchantType(){
this.id = new MerchantTypePk();
}
//Getters and setters here.
}
//MerchantTypePk is a simple Embeddable class here below with two Long fields:
//e1_id and another_id
MerchantMerchantTypeAssociation:
#Entity
class MerchantMerchantTypeAssociation {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "e1_id", column = #Column(name = "e1_id")),
#AttributeOverride(name = "another_id", column = #Column(name = "another_id"))
#AttributeOverride(name = "offer_id", column = #Column(name = "merchant_id"))
})
private MerchantMerchantTypeAssociationPk id;
//******** HERE IS THE QUESTION
//******** HERE IS THE QUESTION
//******** HERE IS THE QUESTION
#ManyToOne
#JoinColumns({
#JoinColumn(name = "e1_id", referencedColumnName = "e1_id", insertable = false, updatable = false),
#JoinColumn(name = "another_id", referencedColumnName = "another_id", insertable = false, updatable = false)
})
#MapsId("e1_id")
#MapsId("another_id")
private MerchantType merchantType;
//Similar mapping to the one above, but with only one Join Column
private Merchant merchant;
//One more local field that is irrelevant to the mapping
//but is the one that is forcing me to map a many - to - many relationship
//in this way.
}
//MerchantMerchantTypeAssociationPk as a simple embeddable
Question: How can I make a mapping for this kind of entities when the annotation '#MapsId' cannot be repeated and it does not accept more than one value?
You did not include the code for MerchantMerchantTypeAssociationPk, but I'm guessing it looks like this:
#Embeddable
public class MerchantMerchantTypeAssociationPk {
public MerchantPk merchantPK;
public MerchantTypePk merchantTypePK;
}
#MapsId is used to specify the attribute within the composite key to which the relationship attribute corresponds, not the columns. So MerchantMerchantTypeAssociation should look like this:
#Entity class MerchantMerchantTypeAssociation {
#EmbeddedId
private MerchantMerchantTypeAssociationPk id;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "e1_id", referencedColumnName = "e1_id",...),
#JoinColumn(name = "e2_id", referencedColumnName = "e2_id",...)
})
#MapsId("merchantTypePK") // <<< *attribute* in Embeddable
private MerchantType merchantType;
#ManyToOne
#JoinColumn(name = "m_id", referencedColumnName = "merchant_id",...)
#MapsId("merchantPK") // <<< *attribute* in Embeddable
private Merchant merchant;
}
Derived identities are discussed in the JPA 2.1 spec, section 2.4.1.
Brand
public class Brand implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "BrandID", nullable = false)
private Integer brandID;
#Basic(optional = false)
#Column(name = "BrandName", nullable = false, length = 100)
private String brandName;
#Basic(optional = false)
#Column(name = "Description", nullable = false, length = 1000)
private String description;
#Column(name = "Is_Visible")
private Boolean isVisible;
#JoinTable(name = "brandcategory", joinColumns = {
#JoinColumn(name = "BrandID", referencedColumnName = "BrandID")}, inverseJoinColumns = {
#JoinColumn(name = "CategoryID", referencedColumnName = "CategoryID")})
#ManyToMany(fetch = FetchType.EAGER)
private Collection<Category> categoryCollection;
#OneToMany(mappedBy = "brand", fetch = FetchType.EAGER)
private Collection<Product> productCollection;
I want to retrive the Brand IDs from table brandcategory whoes categoryID = :categoryID
how can i createnamed query for it in entity brand?
this does not work:
#NamedQuery(name = "Brand.getBrandListByCategory",
query = "SELECT b FROM Brand b WHERE b.brandID =
(SELECT bc.brandID
FROM b.brandctegory bc
WHERE bc.category.categoryID = :categoryID)")
If I understand correctly, you want all the brands belonging to a category. Why don't you simply make the association bidirectional. You could then just do:
Category category = em.find(Category.class, categoryId);
return category.getBrands();
If it's unidirectional, then you'll need a query, but it's much simpler that the one you tried:
select b from Brand b inner join b.categoryCollection category
where category.id = :categoryId;
Your query doesn't make sense: it uses a non-existing association (b.brandcategory). Remember that JPQL uses entities, their persistent fields and associations to other entities. And nothing else. Tables don't exist in JPQL.
AFAIK, you cant go out of a entity boundary, when creating queries in entity class.
Instead use .createNativeQuery() method of the entity manager, to create complex and mixed queries.