I am working on spring hibernate application and trying to delete from a table using non-id many-to-one relationship based column.
Entity classes are:
#Entity
public class Day {
#id(name = "DAY_ID")
dayId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "DAY_ID")
List<Holiday> holidayList;
...
}
#Entity
public class Holiday {
#id(name="HOLIDAY_ID")
holidayId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "DAY_ID")
Day day;
...
}
I am trying to delete a row from holiday table using hql.
String query = "DELETE FROM Holiday WHERE day.dayId = " + dayObject.getdayId();
Query holidayDeleteQuery = getSession().createQuery(query);
holidayDeleteQuery.executeUpdate();
In the console i am getting proper delete query but on checking DB found out that the row is still there but now the DAY_ID column in holiday table is null. I am not able to figure out why is this happening?
EDIT: help!!! My main problem is why DAY_ID column is changing to null value??
I'm not sure that this is your problem, but in your query you say "DELETE FROM Holidays ...", but your Class name is Holiday. In HQL you should be using Class names rather than table names or anything else. Is this typo in your code or just on here?
Actually after looking further there are a few more problems. This is how I'd write it:
String query = "DELETE FROM Holiday h WHERE h.day = :day";
Query holidayDeleteQuery = getSession().createQuery(query);
query.setParameter("day", dayObject);
holidayDeleteQuery.executeUpdate();
To break it down - use the Class name "Holiday", assign it an alias "h" then reference the day field of the Holiday object ("h.day") and compare it to the actual Day object you have.
What is your ONDELETE foreign key constrain? Might it that other part of your application inserting a row?
Related
I believe the code below is working correctly but my logic is flawed and so I'm looking for help getting the results I actually want. In addition I think my attempt on the JPA method is overly convoluted and there's likely a cleaner solution. I know I can likely use #Query but I want to try and get the method name solution working as well so I can learn where I went wrong.
For simplicity I've stripped back the DB tables in this example, however I have Table1 which is the top level table and Table2 which sits below it and has a foreign key back to Table1.
Table 1 -> Table 2 is a one to many relationship, I am looking to write a JPA method that will pull back a record from Table 1 when given the Table1 ID and only the current effective record from Table2 (this will be where EffectiveDateTime column is most recent but NOT future dated). Using the example below I want it to only pull back ID 8.
Unfortunately, I believe with the code I have below it recognises one of those records for Table2 is within the date range required and is therefore pulling all the records back that have a relation with the ID from Table 1. I could be wrong on this though and the logic could be flawed in a different way.
Any help would be appreciated.
Table1 Entity Class
#Entity
#Table(name = "TABLE1")
public class Table1 {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, unique = true)
private Integer id;
#OneToMany(mappedBy = "table1", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(value = FetchMode.SUBSELECT)
#JsonManagedReference
private List<Table2> table2;
//Plus getters & setters
Table2 Entity Class
#Entity
#Table(name = "TABLE2")
public class Table2{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, unique = true)
private Integer id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "Table1Id")
#JsonBackReference
private Table1 table1;
#Column(name = "EffectiveDateTime", nullable = false)
private LocalDateTime effectiveDateTime;
//Plus getters & setters
Repo Class
#Repository
public interface Repository extends JpaRepository<Table1, Integer>, JpaSpecificationExecutor<Table1> {
public Optional<Table1> findTopByIdAndTable2EffectiveDateTimeLessThanEqualOrderByTable2EffectiveDateTimeDesc(int id, LocalDateTime now);
}
Current JSON return
{
"id": 5
"Table2": [
{
"id": 6,
"effectiveDateTime": "2021-01-01T00:00:01"
},
{
"id": 8,
"effectiveDateTime": "2022-01-01T00:00:01"
},
{
"id": 9,
"effectiveDateTime": "2023-01-01T00:00:01"
}
]
}
Once you declared a oneToMany relationship, your entity will load all joined entity when calling the getTable method (wich is called by jackson when serializing). I believe if you activate debug mode, you will see 2 request, the first will be the one you describe in your repopsitory, the second the one JPA use to load all related table2 entity.
Their is some possible ways to accomplish what you want:
the first (and most obvious ?) is to manually make the 2 request equivalent to your HQL request (the table 1 find by ID and then the table 2 find by....) and then aggregate the 2 results
the second one would be to use jackson to serialize your data how you want to (by filtering useless table 2 data)
the third one would be to use jackson to serialize a table2 (wich give you the corresponding table1 data) into the appropriate JSON. It's a bit more complicated but probably more efficient if your table1 reference a lot of table2 data.
The last one would be to try using projections it allow you to declare only the data you want to retrieve when requesting data.
hope this help !
In the end we completed this using a JPQL query. An example of the query is below in case it helps any others in the future.
#Query(value = "SELECT table1 FROM Table1 table1 "
+ " JOIN FETCH table1.table2 table2 "
+ " WHERE table1.id = :table1Id"
+ " AND table2.id = "
+ " ( SELECT table2.id FROM table2 "
+ " WHERE table2.table1.id = :table1Id "
+ " AND table2.effectiveDateTime = "
+ " ( SELECT MAX(effectiveDateTime) FROM table2"
+ " WHERE table2.table1.id = :table1Id "
+ " AND table2.effectiveDateTime <= :currentTimestamp "
+ " ) "
+ " ) ")
Optional<Table1> getEffectiveDateTimeTable2(#Param("table1Id") int table1Id, #Param("currentTimestamp") LocalDateTime currentTimestamp);
Usually during my work hours i spend a lot of time querying the db(oracle) and parsing blob from various table where the streams that we receive are stored.
There are various type of stream so i was trying to made a simple webapp where i write the select statement and it returns all the stream parsed accordingly.
My problem is that using jpa and executing the simple native query:
select B_BODY from TABLE_B where TRANSACTION_ID = 'GG-148c-01502790743907855009';
the statement doesn't return anything but querying directly the database return the record.
this is my java code:
#Transactional(readOnly = true)
public List<Object[]> retrieveBlobs(String squery) {
squery = squery + " and rownum <= "+maxResults;
Query query = em.createNativeQuery(squery);
List<Object[]> resultList = query.getResultList();
return resultList;
}
this is the sql generated:
Hibernate:
select
B_BODY
from
TABLE_B
where
TRANSACTION_ID ='GG-148c-01502790743907855009'
and rownum <= 100
i know that this way might seems weird but our team spend a lot of time trying to tokenize the stored streams(the code that identify how to parse the stream is also stored in the tables).Useless to say this application is going to be used only internally.there is a way to just execute the query as it is and retrieve the correct output?
Well, I tried to reproduce your problem on MariaDB (with mysql-connector-java + hibernate) but selecting a lob with native query was working properly.
You can try to create entities which will be holding your blob and check if this would help. Just make a standard entity with #Lob annotation over your lob column.
#Entity
#NamedQueries(
#NamedQuery(name = FIND_ALL, query = "SELECT m FROM LobEntity m")
)
public class LobEntity {
public static final String FIND_ALL = "PhpEntity.findAll";
#Id
#Column(name = "id")
private String id;
#Lob
#Column(name = "lob")
private byte[] lob;
//Use Blob class if you want to use streams.
//#Column(name = "lob")
//#Lob
//private Blob lob;
}
I have created three tables in Oracle SQL Developer namely
1.Test_Employee2
2.Test_Project2
3.Employee_Project2.
The table Employee_Project2 is the join table as the relation between Test_Project2 and Employee_Project2 is Many-To-Many.
In hibernate I created to two hibernate classes TestEmployee and TestProject for Test_Project2 and Employee_Project2 tables respectively,
and the table Employee_Project2 was defined in TestProject hibernate class as follows:
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "Employee_Project2", joinColumns = #JoinColumn(name = "proj_id"), inverseJoinColumns = #JoinColumn(name = "emp_id"))
private Set<TestEmployee> employeesList;
I populated the tables Test_Project2 and Employee_Project2 with some records, and the join table Employee_Project2 automatically got populated with some records.
now the problem I am facing currently is, I want to use a simple select statement on the join table Employee_Project2 using hiberante as follows:
String hql = "FROM Employee_Project2";
Query query = session.createQuery(hql);
List results = query.list();
for (Object row : results) {
//what to do here
}
how can I do that despite the join table 'Employee_Project2' is not a hibernate class.?
update:
I would like to retrieve all the records in the hibernate table "TestProject", so i wrote the following code
String hql = "FROM TestProject";
Query query = session.createQuery(hql);
List results = query.list();
System.out.println("results.get(0)" + results.get(0).toString());
now the problem is, at run time i receive something like the following
results.get(0)msc.hibernate.persistence.TestProject#12ec9534
how can i get the values contained in the each row??
What you want to do is to create typed query. With proper mapping you can get related objects as well - no need to query join tables as ORM will do this for you:
Query query = session.createQuery(hql);
List<TestProject> results = query.list();
for (TestProject row : results) {
//what to do here
// do whatever you want
}
And with propper relation mapping you can get relations like this:
for (TestProject row : results) {
Set<TestEmployee> employees=row.getEmployeesList();
// do more work.
}
As for "how to"s - the topic is too broad to cover it in single answer etc. but you should be able to start from here - http://hibernate.org/orm/documentation/5.1/
I am getting an error with the following JPQL query:
#NamedQuery (name = "Customer.getById", query =
"SELECT o
FROM bub.Customer o
WHERE o.user_id = :myid")
[bub.Customer is the #Entity name]
This is an excerpt of the error message I'm receiving:
org.hibernate.HibernateException:
Errors in named queries:
Customer.getById\n
Caused by: org.hibernate.HibernateException:
Errors in named queries: Customer.getById
When I remove the WHERE clause Wildfly allows me to deploy my web app so I know there is something wrong wtih my WHERE clause. Specifically since the column name is user_id in my Customer table I believe there may be an issue with the underscore(_) in the JPQL. I've tried changing the WHERE clause to "WHERE o.userId = :myid" but that didn't work either.
How can I fix the WHERE clause so my website will deploy and still work the correct way?
EDIT:
The relevant method is this:
public static Customer getById (final EntityManager em, final long id)
{
return em.createNamedQuery ("Customer.getById", Customer.class).setParameter ("myid", id).getSingleResult ();
}
I don't think this is the issue though.
EDIT2:
It turns out this was the issue:
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn (name = "user_id")
private User user;
I ended up changing the JPQL query to this and now it's working:
#NamedQuery (name = "Customer.getById", query =
"SELECT o
FROM bub.Customer o
WHERE o.user = :myid")
In JPQL you don't use the column name, but the attribute name(unless you annotate something else)
I am currently working on a project to transfer some legacy jdbc select statements over to using Hibernate and it's criteria api.
The two relevant table columns and the SQL query looks like:
-QUERIES-
primaryId
-QUERYDETAILS-
primaryId
linkedQueryId -> Foreign key references queries.primaryId
value1
value2
select *
from queries q
where q.primaryId not in (SELECT qd.linkedQueryId
FROM querydetails qd
WHERE (qd.value1 LIKE 'PROMPT%'
OR qd.value2 LIKE 'PROMPT%'));
My entity relationships look like:
#Table("queries")
public class QueryEntity{
#Id
#Column
private Long primaryId;
#OneToMany(targetEntity = QueryDetailEntity.class, mappedBy = "query", fetch = FetchType.EAGER)
private Set<QueryDetailEntities> queryDetails;
//..getters/setters..
}
#Entity
#Table(name = "queryDetails")
public class QueryDetailEntity {
#Id
#Column
private Long primaryId;
#ManyToOne(targetEntity = QueryEntity.class)
private QueryEntity query;
#Column(name="value1")
private String value1;
#Column(name="value2")
private String value2;
//..getters/setters..
}
I am attempting to utilize the criteria api in this way:
Criteria crit = sessionFactory.getCurrentSession().createCriteria(QueryEntity.class);
DetachedCriteria subQuery = DetachedCriteria.forClass(QueryDetailEntity.class);
LogicalExpression hasPrompt = Restrictions.or(Restrictions.ilike("value1", "PROMPT%"),
Restrictions.ilike("value2", "PROMPT%"));
subQuery.add(hasPrompt);
Criterion subQueryCrit = Subqueries.notIn("queryDetails", subQuery);
crit.add(subQueryCrit);
List<QueryMainEntity> entities = (List<QueryMainEntity>) crit.list();
System.out.println("# of results = " + entities.size());
I am getting a NullPointerException on the crit.list() line that looks like
Exception in thread "main" java.lang.NullPointerException
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getProjectedTypes(CriteriaQueryTranslator.java:362)
at org.hibernate.criterion.SubqueryExpression.createAndSetInnerQuery(SubqueryExpression.java:153)
at org.hibernate.criterion.SubqueryExpression.toSqlString(SubqueryExpression.java:69)
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getWhereCondition(CriteriaQueryTranslator.java:380)
at org.hibernate.loader.criteria.CriteriaJoinWalker.<init>(CriteriaJoinWalker.java:114)
at org.hibernate.loader.criteria.CriteriaJoinWalker.<init>(CriteriaJoinWalker.java:83)
at org.hibernate.loader.criteria.CriteriaLoader.<init>(CriteriaLoader.java:92)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1687)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347)
Now, I think its pretty safe to say I'm using the Criteria Api/Detached Query Api incorrectly, but I'm not sure what the 'correct' way to do it is since the Hibernate Docs only briefly cover criteria api subqueries.
I realize this is a pretty long question, but I figure its appear to put it all the relevant aspects of the question (query I'm attempting to represent via Criteria API, tables, entities).
Give this a shot:
DetachedCriteria d = DetachedCriteria.forClass(QueryDetailEntity.class, "qd");
d.setProjection(Projections.projectionList().add(Projections.property("qd.query")));
d.add(Restrictions.or(Restrictions.like("qd.value1", "PROMPT%"), Restrictions.like("qd.value2", "PROMPT%")));
criteria = session.createCriteria(QueryEntity.class, "q");
criteria.add(Subqueries.propertyNotIn("q.primaryId", d));
criteria.list();
The use of the following are property names, not column names:
qd.query
qd.value1
qd.value2
q.primaryId
As a side note, if this is not a dynamically generated query, have you given thought to using HQL instead?