How to create a namedquery of manytomany entity? - java

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.

Related

Multiple queries executed by Hibernate with #OneToOne and #JoinColumnsOrFormulas

I have a table that contains personal data. This can be referenced by different tables.
PersonalData.java
#Entity
#Table(name = "personal_information")
#Getter
#Setter
public class PersonalInformation implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "personal_information_no")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "personal_information_seq")
#SequenceGenerator(name = "personal_information_seq", sequenceName = "personal_information_seq", allocationSize = 1, initialValue = 1)
private int personalInformationNo;
#Column(name = "ref_object_type")
private String refObjectType;
#Column(name = "ref_object_no")
private int refObjectNo;
#Column(name = "type")
private String type;
}
Staff.java
#Entity
#Table(name = "staff")
public class Staff {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "staff_no", unique = true, nullable = false)
private int staffNo;
#OneToOne(optional = false, fetch = FetchType.LAZY)
#LazyToOne(LazyToOneOption.NO_PROXY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column = #JoinColumn( name = "staff_no", referencedColumnName = "ref_object_no", insertable=false, updatable=false)),
#JoinColumnOrFormula(formula = #JoinFormula( value = "(SELECT 'staff')", referencedColumnName = "ref_object_type")),
#JoinColumnOrFormula(formula = #JoinFormula( value = "(SELECT 'work')", referencedColumnName = "type"))
})
private PersonalInformation workPersonalInformation;
#OneToOne(optional = false, fetch = FetchType.LAZY)
#LazyToOne(LazyToOneOption.NO_PROXY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column = #JoinColumn( name = "staff_no", referencedColumnName = "ref_object_no", insertable=false, updatable=false)),
#JoinColumnOrFormula(formula = #JoinFormula( value = "(SELECT 'staff')", referencedColumnName = "ref_object_type")),
#JoinColumnOrFormula(formula = #JoinFormula( value = "(SELECT 'private')", referencedColumnName = "type"))
})
private PersonalInformation privatePersonalInformation;
}
User.java
#Entity
#Table(name = "user")
public class BusinessProviderUser {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_no", unique = true, nullable = false)
private int userNo;
#Column(name = "staff_no")
private Integer staffNo;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column = #JoinColumn( name = "user_no", referencedColumnName = "ref_object_no", insertable=false, updatable=false)),
#JoinColumnOrFormula(formula = #JoinFormula( value = "(SELECT 'user')", referencedColumnName = "ref_object_type"))
})
private PersonalInformation personalInformation;
}
As you can see, the reference is a bit more complex. This problem was solved with the annotation #JoinColumnsOrFormulas. The join to the person data is annotated #OneToOne in the referencing table.
The problem now is that each time the referencing table is called, additional queries are always executed.
How can I prevent this so that everything is executed in one query?
It is known that Hibernate does not support lazy loading with #OneToOne. One approach here was to implement lazy loading using bytecode enhencment. Unfortunately without success.
#OneToOne(fetch = FetchType.LAZY, optional = false)
#LazyToOne(LazyToOneOption.NO_PROXY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column = #JoinColumn( name = "user_no", referencedColumnName = "ref_object_no", insertable=false, updatable=false)),
#JoinColumnOrFormula(formula = #JoinFormula( value = "(SELECT 'user')", referencedColumnName = "ref_object_type"))
})
private PersonalInformation personalInformation;
Another approach is to load everything in one single query by adding #Fetch(FetchMode.Join).
#OneToOne(optional = false)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(column = #JoinColumn( name = "user_no", referencedColumnName = "ref_object_no", insertable=false, updatable=false)),
#JoinColumnOrFormula(formula = #JoinFormula( value = "(SELECT 'user')", referencedColumnName = "ref_object_type"))
})
#Fetch(FetchMode.JOIN)
private PersonalInformation personalInformation;
In the end, it is not crucial for the project whether the data is loaded lazy (preferred) or immediately.
It is only important that multiple queries are not sent per user or staff.
Are there any tips or solutions for the problem?
Maybe there is also a nicer solution regarding the database structure? Changes could be made here as well.
Unfortunately, Hibernate version 3.6.10-final must still be used.

"MultipleBagFetchException: cannot simultaneously fetch multiple bags" when joining three depth table

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;
}

hibernate select query from child table

I am pretty new to hibernate. I have a two tables having one to many relationship. Two tables are:
public class Pashmina implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "sq_pashmina_id")
#SequenceGenerator(name = "sq_pashmina_id", sequenceName = "sq_pashmina_id")
#Column(name = "PASHMINA_ID")
private int pashminaId;
#Column(name = "PASHMINA_NAME")
private String pashminaName;
#Column(name = "PRICE")
private double price;
#Column(name = "ADDED_AT", insertable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date addedAt;
#Column(name = "CATEGORY")
private String category;
#Column(name = "ENABLED", insertable = false)
private Character enabled;
#OneToMany(mappedBy = "colourId", fetch = FetchType.EAGER)
private Set<PashminaColour> pashminaColor = new HashSet<PashminaColour>();
#OneToMany(mappedBy = "imageId", fetch = FetchType.EAGER)
private Set<Image> images = new HashSet<Image>();
#OneToMany(mappedBy = "descriptionId", fetch = FetchType.EAGER)
private Set<Description> descriptions = new HashSet<Description>();
//getter and setter method
This is a parent class and it have one to many relationship with Image table
public class Image implements Serializable {
#Id
#Column(name = "IMAGE_ID")
private int imageId;
#Column(name = "IMAGE_NAME")
private String imageName;
#JoinColumn(name = "PASHMINA_ID", referencedColumnName = "PASHMINA_ID")
#ManyToOne
private Pashmina pashmina;
Now I want to select a imagenames from Image class using its parent class's id (ie: pashminaId)
eg:
SELECT IMAGE_NAME FROM TBL_IMAGE WHERE PASHMINA_ID = 'some_digit';
How can i pass pashminaId in image class as there is no pashminaId it only have an Object creation of Parent class Pashmina.
So, How can i achieve this in hibernate?
Let me know if you don't understand me.
Thanks! Hoping for positive response.
As Hibernate treats objects instead of SQL tables, you can simply access the pashmina object from your image object, and in your query you will be treating java objects/POJOs, so you can acccess it via Image.pashmina.pashminaId.
So you can write the following query:
String hql = "select I.imageName FROM Image I WHERE I.pashmina.pashminaId = 10";
Query query = session.createQuery(hql);
List results = query.list();

The abstract schema type 'User_Book' is unknown

I have a database with several entities, in particular Book and User. Between them there exists a ManyToMany relationship like this:
Book:
#Entity
#Table(name = "Books")
public class Book implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "bookId", nullable = false, unique = true)
private Long id;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "price", nullable = false)
private int price;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_Book",
joinColumns = #JoinColumn(name = "bookId"),
inverseJoinColumns = #JoinColumn(name = "userId"))
private Set<UserAccount> users;
User:
#Entity
#Table(name = "UserAccounts")
public class UserAccount implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "userId", nullable = false, unique = true)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "password", nullable = false)
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_Book",
joinColumns = #JoinColumn(name = "userId"),
inverseJoinColumns = #JoinColumn(name = "bookId"))
Set<Book> purchasedBooks;
Everything works fine, the table User_Book is indeed created in the database. The problem seems to be related to the access of this Table.
For example,
Query query = entityManager.createQuery("SELECT u FROM User_Book u");
keeps telling me the following:
The abstract schema type 'User_Book' is unknown
So, shall I create from scratch the User_Book entity? Will it get automtically populated like now, that is, whenever a user buys a book, will this purchase be recorded in the table?
User_Book is not an entity. Therefore you cannot use createQuery, BUT you can use createNativeQuery to execute a SQL query:
Query query = entityManager.createNativeQuery("SELECT * FROM User_Book");
The result will be List<Object[]>

create entity from database wizard jpa 2.1 #ManyToMany, fix list not needed

i have some join tables like this multimedia_feature.
The wizard will create in Multimedia class a List attribute:
...
#JoinTable(name = "multimedia_feature", joinColumns = {
#JoinColumn(name = "feature_oid", referencedColumnName = "oid")}, inverseJoinColumns = {
#JoinColumn(name = "multimedia_oid", referencedColumnName = "oid")})
#ManyToMany
private List<Feature> featureList;
...
since i just need a ManyToOne relationship (one feature can have many multimedia file), i flagged multimedia_oid as UNIQUE.
After this the wizard creates other 2 tables (i think redoundant)
#Entity
#Table(name = "multimedia_feature")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "MultimediaFeature.findAll", query = "SELECT m FROM MultimediaFeature m"),
#NamedQuery(name = "MultimediaFeature.findByMultimediaOid", query = "SELECT m FROM MultimediaFeature m WHERE m.multimediaFeaturePK.multimediaOid = :multimediaOid"),
#NamedQuery(name = "MultimediaFeature.findByFeatureOid", query = "SELECT m FROM MultimediaFeature m WHERE m.multimediaFeaturePK.featureOid = :featureOid")})
public class MultimediaFeature implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected MultimediaFeaturePK multimediaFeaturePK;
#JoinColumn(name = "multimedia_oid", referencedColumnName = "oid", insertable = false, updatable = false)
#OneToOne(optional = false)
private Multimedia multimedia;
#JoinColumn(name = "feature_oid", referencedColumnName = "oid", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Feature feature;
...
...
and
#Embeddable
public class MultimediaFeaturePK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "multimedia_oid")
private int multimediaOid;
#Basic(optional = false)
#NotNull
#Column(name = "feature_oid")
private int featureOid;
...
...
finally it added an attribute in Multimedia class:
....
#OneToOne(cascade = CascadeType.ALL, mappedBy = "multimedia")
private MultimediaFeature multimediaFeature;
....
since I have really many join classes, I would avoid creating all of these classes.
Can i manually create the attributes, such as:
#JoinTable(name = "multimedia_feature",
#JoinColumn(name"feature_oid", referencedColumnName = "oid")
)
#OneToOne(optional = false)
private Feature feature;
or this precludes the correct persistence?
It looks the feature attribute in Multimedia class should be a #ManyToOne relationship.
By default, join tables are created for the mapping of many-to-many relationships and unidirectional one-to-many relationships.
If you want to avoid the join class, I think you can map the multimedia attribute in Feature class by using the #JoinTable like that:
#OneToMany
#JoinTable(name = "multimedia_feature",
joinColumns = #JoinColumn(name = "feature_oid"),
inverseJoinColumns = #JoinColumn(name = "multimedia_oid") )
private List<Multimedia> multimediaList;
If you do need bidirectional relationship with join table, the mapping will be like this:
public class Feature implements Serializable {
#OneToMany(mappedBy="feature")
private List<Multimedia> multimediaList;
...
}
public class Multimedia implements Serializable {
#ManyToOne
#JoinTable(name = "multimedia_feature",
joinColumns = #JoinColumn(name = "multimedia_oid") ,
inverseJoinColumns = #JoinColumn(name = "feature_oid"))
private Feature feature;
...
}
Or you can totally remove the join table with a bidirectional association by introducing a join column in multimedia table, like feture_oid. So that you can easily map the feature attribute in Multimedia class:
#ManyToOne
#JoinColumn(name = "feature_oid")
private Feature feature;
And in the Feature class will be like:
#OneToMany(mappedBy="feature")
private List<Multimedia> multimediaList;

Categories

Resources