Problem when using Spring Specification with One-To-Many relationship - java

I'm having a problem with Specification as stated in the title.
Here is my FishingLocation class:
#Entity
#Table(name = "tbl_fishing_location")
public class FishingLocation {
...
#JsonIgnore
#OneToMany(mappedBy = "id", fetch = FetchType.EAGER)
private List<Lake> lakeList;
...
}
And here is my Lake class:
#Entity
#Table(name = "tbl_lake")
public class Lake {
...
#JsonIgnore
#ManyToOne
#JoinColumn
private FishingLocation fishingLocation;
...
}
They both have a StaticMetamodel as follow:
#StaticMetamodel(FishingLocation.class)
public class FishingLocation_ {
public static volatile ListAttribute<FishingLocation, Lake> lakeList;
public static volatile SingularAttribute<FishingLocation, Long> id;
}
#StaticMetamodel(Lake.class)
public class Lake_ {
public static volatile SingularAttribute<Lake, Long> id;
public static volatile SingularAttribute<Lake, FishingLocation> fishingLocation;
}
I have created a Specification to filter FishingLocation as follow (the fishing method is not relevant):
public static Specification<FishingLocation> fishingMethodIdIn(Set<Long> fishingMethodIds) {
if (fishingMethodIds == null || fishingMethodIds.isEmpty()) {
return null;
}
return root.join(FishingLocation_.lakeList)
.join(Lake_.fishingMethodList)
.get(FishingMethod_.id).in(fishingMethodIds);
};
}
The problem is that when I run the program and send a request to filter, Hibernate showed me this SQL query:
select
fishingloc0_.id as id1_7_,
fishingloc0_.active as active2_7_,
...
from
tbl_fishing_location fishingloc0_
.... (some inner joins)
inner join
tbl_lake lakelist4_
on fishingloc0_.id=lakelist4_.id
....
It is supposed to be on fishingloc0_.id=lakelist4_.fishing_location_id. So where is the problem in my code? Huge thanks to anyone helping me with this problem.
Edit: This is just a small fraction of my code. My filter has multiple criteria and join many tables so I cannot just use premade function in FishingLocationRepository. I want to create dynamic query which should be build depend on what user chooses to filter by.

you don't need to call this all using custom queries the Hibernate will take care of everything with OneTO Many and ManyTOOne mappings.
You can get the required data by simply calling the getter methods of its class.
for example, in the case of Fishing Location, you can get all the lakes related to that location by calling new FishingLocation().getLakeList().
same for the other case. https://www.javatpoint.com/hibernate-many-to-one-example-using-annotation

Related

Hibernate: Fetch all records from a single table, regardless of type defined with #DiscriminatorValue

Hibernate has powerful mechanism to make several entities to share same table in the database. For me, being able to fetch all records regardless of the subtypes would be the most important thing. However this doesn't seem to work.
Provided we have some standard setup:
#Entity
#Table(name = "plane")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="planetype",
discriminatorType=DiscriminatorType.STRING
)
public abstract class Plane { ... }
#Entity
#Table(name = "plane")
#DiscriminatorValue("A320")
public class A320 extends Plane { ... }
#Entity
#Table(name = "plane")
#DiscriminatorValue("B777")
public class B777 extends Plane { ... }
What do I need to access all the planes we have on the table?
#Entity
#DiscriminatorValue("")
public class AnyPlane extends Plane { ... } // this doesn't work
#Entity
public class AnyPlane extends Plane { ... } // this doesn't work either
I try to fetch AnyPlanes same way I usually do...
CriteriaBuilder cb = getSession().getCriteriaBuilder();
CriteriaQuery<AnyPlane> q = cb.createQuery(AnyPlane.class);
Root<AnyPlane> root = q.from(AnyPlane.class);
Query<AnyPlane> query = getSession().createQuery(q.select(root)
.where(someRestrictions)
);
return query.list();
...and get empty list, because Hibernate considers even empty string to be a valid denominator. What would I do?
Worth to mention, that the planes already happyly fly up the code stack, and my goal is to refactor the DB layer without introducing much changes. I'm also not keen to use native SQL.
Simple solution I found is to make the AnyPlane to fly on it own, without extending the Plane:
#Entity
#Table(name = "plane")
public class AnyPlane {
... // same fields we would have on the parent
#Enumerated(EnumType.STRING)
#Column(name = "planetype", insertable = false, updatable = false)
private PlaneType planeType;
...
}
public enum LikedEntityType {
A320,
B777
}
Also consider making a common parent for Plane and AnyPlane to avoid the copypaste for the common fields, while not bearing the sins of #DiscriminatorColumn.

Hibernate - #OneToOne mappedBy one of multiple class fields

I have class hierarchy like this:
public abstract class CommunicationCampaign {...}
public class EmailCampaign extends CommunicationCampaign {...}
public class SmsCampaign extends CommunicationCampaign {...}
public class Trigger {...}
CommunicationCampaign defines what kind of message should be distribute to our clients and Trigger defines when communication should be distributed. Campaign should point to two triggers, test trigger and production trigger. The code for this look like this:
public abstract class CommunicationCampaign {
...
#OneToOne(targetEntity = Trigger.class, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn(name = "TEST_TRIGGER_ID")
private Trigger testTrigger;
#OneToOne(targetEntity = Trigger.class, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn(name = "TRIGGER_ID")
private Trigger trigger;
...
}
My question is how should i map this relation on the Trigger side? This is the first time I'm facing such problem, so I would appreciate any advice. Maybe I shouldn't approach this using #OneToOne mapping, however I would like to avoid solution with #OneToMany mapping, because then I would have to add new column to Trigger indicating if it is test trigger or not. Trigger behavior should be exactly the same regardless it is used as test or regular trigger for specific campaign. Also Trigger is related to other entities in database and I want to do as little modification as possible to it.
public class Trigger {
...
/*How to map relation to SmsCampaign and EmailCampaign?*/
...
}
I'm looking for something like this:
public class Trigger {
...
#OneToOne(mappedBy = "testTrigger or trigger", fetch = FetchType.LAZY)
private EmailCampaign emailCampaign;
#OneToOne(mappedBy = "testTrigger or trigger", fetch = FetchType.LAZY)
private SmsCampaign smsCampaign;
...
}

Logical delete at a common place in hibernate

I am using Spring and Hibernate for my application.
I am only allowing logical delete in my application where I need to set the field isActive=false. Instead of repeating the same field in all the entities, I created a Base Class with the property and getter-setter for 'isActive'.
So, during delete, I invoke the update() method and set the isActive to false.
I am not able to get this working. If any one has any idea, please let me know.
Base Entity
public abstract class BaseEntity<TId extends Serializable> implements IEntity<TId> {
#Basic
#Column(name = "IsActive")
protected boolean isActive;
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
isActive= isActive;
}
}
Child Entity
#Entity(name="Role")
#Table(schema = "dbo")
public class MyEntity extends BaseEntity {
//remaining entities
}
Hibernate Util Class
public void remove(TEntity entity) {
//Note: Enterprise data should be never removed.
entity.setIsActive(false);
sessionFactory.getCurrentSession().update(entity);
}
Try to replace the code in setIsActive method with:
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
in your code the use of variable name without this could be ambiguos...
I think you should also add #MappedSuperclass annotation to your abstract class to achieve field inheritance.
The issue with the proposed solution (which you allude to in your comment to that answer) is that does not handle cascading delete.
An alternative (Hibernate specific, non-JPA) solution might be to use Hibernate's #SQLDelete annotation:
http://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/querysql.html#querysql-cud
I seem to recall however that this Annotation cannot be defined on the Superclass and must be defined on each Entity class.
The problem with Logical delete in general however is that you then have to remember to filter every single query and every single collection mapping to exclude these records.
In my opinion an even better solution is to forget about logical delete altogether. Use Hibernate Envers as an auditing mechanism. You can then recover any deleted records as required.
http://envers.jboss.org/
You can use the SQLDelete annotation...
#org.hibernate.annotations.SQLDelete;
//Package name...
//Imports...
#Entity
#Table(name = "CUSTOMER")
//Override the default Hibernation delete and set the deleted flag rather than deleting the record from the db.
#SQLDelete(sql="UPDATE customer SET deleted = '1' WHERE id = ?")
//Filter added to retrieve only records that have not been soft deleted.
#Where(clause="deleted <> '1'")
public class Customer implements java.io.Serializable {
private long id;
...
private char deleted;
Source: http://featurenotbug.com/2009/07/soft-deletes-using-hibernate-annotations/

CascadeType doesn't work in in-memory database

I have a webapp with database where two Entities have many-to-many relationship but I implemented join table manually. When one of the entities gets deleted it deletes all entries in the join table and updates the other entity so all works perfectly fine, but now I'm supposed to write a test for this feature. For tests I am using in-memory database and that's really the only difference, the same methods with the same annotations (and cascade types) are called but I keep getting:
org.hibernate.exception.ConstraintViolationException: integrity constraint violation: foreign key no action; FKC17477FD8940DF2B table ENTITY1_ENTITY2
I didn't paste any code as I don't believe there is anything wrong with it since it's working. I don't ask to fix this for me, I just need to know what is likely to cause this kind of behavior because I've just ran out of ideas and I don't know what else to search for... Thanks
EDIT: here's some code:
#Entity
#Table(name = "interviewer")
public class Interviewer implements Identifiable {
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "interviewer_id")
private Collection<InterviewerTechnology> technologies;
}
#Entity
#Table(name = "technology")
public class Technology implements Identifiable {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "technology_id")
private Collection<InterviewerTechnology> technologies;
}
#Entity
#Table(name = "interviewer_technology")
public class InterviewerTechnology implements Identifiable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
private Interviewer interviewer;
#ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
private Technology technology;
}
#Component
public class TechnologyDao extends AbstractEntityDao<Technology> {
public void remove(Integer id) {
Technology technology = find(id);
em.remove(technology);
}
}
This code does exactly what I want it to do, it just seems like database used for tests does not see CascadeType parameters that do all the job here
I have found the problem and it was #Transactional annotation. All my test DAOs were extending generic test DAO which was annotated with #Transactional and I blindly annotated every single DAO with it again. The problem here is that some operations need to be performed as single transactions (may need flush() after being executed) so that data is available for other operations straight away. Consider following example:
#Transactional
public abstract class AbstractEntityDao<E> {
#PersistenceContext(unitName = "some-persistence")
protected EntityManager em;
public E create(E e) {
em.persist(e);
return e;
}
(...)
}
which means that every method in this class is a transaction. Now if we annotate another class that extends this class with #Transactional every method will be another transaction, which means if we delete several things in one method it should take several transactions (they all need flush() method to be called in order to execute cascade) but instead they will run as one transaction (unless we specify Propagation). Let this be a lesson for everyone (especially me) to think carefully about which operations need separate transactions and which can be executed as one.

How to create Many-One Mapping in hibernate?

I want to create Many-One Mapping between two tabels, Expense(ID, NAME, CATEGORY) and
Category(ID, NAME).
In my class i have created a field 'Category category' and its setters and getters.
I did them after seeing some stuff from internet. What are all the changes i have to do in my Category.java class. For now, its looks like,
public class Category{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int catId;
private String catName;
public Category() {
}
public int getCatId() {
return this.catId;
}
public void setCatId(int catId) {
this.catId = catId;
}
public String getCatName() {
return this.catName;
}
public void setCatName(String catName) {
this.catName = catName;
}
}
I dont want to do mappings with xml config. I think, annotations is good for a beginner like me.
And my Old! SQL query looks like,
SELECT EXPENSES.EXPNS_ID, EXPENSES.CAT_ID, EXPENSES.NAME, CATEGORY.CAT_NAME FROM EXPENSES INNER JOIN CATEGORY ON EXPENSES.CAT_ID = CATEGORY.CAT_ID WHERE USER_NAME="+currentUserName
How to use inner join in Hibernate?
Any Suggestions!!
Thanks!
Update
Thanks for all answerers,
I tried what you told and it returns a empty list.
To, test i set the 'userName=Tamil' which is in the table.
The query generated by Hibernate is looks like below,
select expens0_.expnsId as expnsId1_, expens0_.catId as catId1_, expens0_.category_catId as category7_1_, expens0_.userName as userName1_ from Expens expens0_ inner join Category category1_ on expens0_.category_catId=category1_.catId where expens0_.userName=?
As a beginner, i have some doubts in JPQL, I want catName from Category[catId, catName] table. And the catId is also available in Expens[expnsId, catId, userName].
By adding the below lines in Expens.java class, how it will give me catName along with the other variables in the Expens table.
#ManyToOne
private Category category
// getters, setters
I cant able to understand it. Without understanding this i cant move further, i have to give more mappings in my project. If clear with this mapping, i can move to the rest with confidence.
The query i used is pascal's version: Query query = hSession.createQuery("SELECT e FROM Expens e JOIN e.category c WHERE e.userName = :userName").setParameter("userName", userName);
For me, the query generated by hibernate is looks like same as my Old SQl query. I cant able to find problem here.
Actually, a big part of the documentation that would be useful in your case is located in the Hibernate Annotations Reference Guides (links provided below). Reading it would be very worth it.
That being said, regarding your specific question, the simplest possible mapping would be:
#Entity
public class Expense {
#Id #GeneratedValue
private Long;
#ManyToOne
private Category category
// getters, setters
...
}
That's all.
If you want to make it bi-directional, you'll have to add a OneToMany on the other side (and don't forget the mappedBy element since the association is bidirectional):
#Entity
public class Category {
#Id #GeneratedValue
private Long id;
#OneToMany(mappedBy="category")
private Set<Expense> expenses = new HashSet<Expense>();
....
}
And a possible JPQL query would be:
SELECT e FROM Expense e JOIN e.category c WHERE e.username = :username
Update: Hibernate and JDBC are different. With Hibernate, you need to think objects and the above HQL query (which was more an example) will actually return a List<Expense>. To get a category name, iterate over the results and navigate through the association. For example:
List<Expense> expenses = ... // some code to retrieve a list by username
for (Expense expense : expenses) {
System.out.println(expense.getCategory().getName());
}
References
2.2. Mapping with JPA (Java Persistence Annotations)
2.2.5.2. Many-to-one
As Bozho suggested,
#ManyToOne(fetch=FetchType.EAGER) // Gonna be eager by default anyway
#JoinColumn(name="CATEGORY_ID")
private Category category;
Plus this in your Category class to make it bidirectional,
#OneToMany(mappedBy="category")
private List<Expense> expense;
You need not do an inner join like that. When you query the expense, the related category will automatically get loaded eagerly, most likely using join.
In your Expense class have:
#ManyToOne
#JoinColumn(name="CATEGORY_ID")
private Category category
As pointed in the comments, if you need to access all expenses in a given category, i.e. have the one-to-many relationship, you can have:
#OneToMany
private List<Expense> expenses;
I, for example, prefer to use as little #OneToMany mappings as possible - you'd have to manager eager/lazy loading, at some point limiting the number of results, etc. For them I tend to use HQL queries that fetch the subset of objects (expenses in your case) that I need.

Categories