I have class A and class B.
Now class A has Set of B.
class A{
Set<B> bset //
}
bset.size() can be > 100 or > 500 etc
Now i want to change one filed of all B which corresponds to A
Eg:update B set Bfield = x where id IN (1,2,3);
1)What will be the query in hibernate?
2)1,2,3 are ids of B (B.id).
3)How can i retrive all ids of B that corresponds to A in the format required by the query?
If I understood you correct
update B set b.field = :newValue where b.parent = :parentA
Related
I am using hibernate-orm with spring-data-jpa. I have three entities A, B, C declared as follows:
#Entity
public class A {
#OneToMany(....)
private List<B> listOfB;
}
#Entity
public class B {
#ManyToOne(...)
private A a;
#OneToMany(...)
private List<C> listOfC;
}
#Entity
public class C {
#ManyToOne(...)
private B b;
}
My objective is to get A and fetch listOfB as well, with some condition on entity C without fetching it. The following JPQL is working fine.
SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
LEFT JOIN b.listOfC c
WHERE c.xyz = :xyz
When I tried using JPA Specification my Specification it looks like the following:
(rootA, query, builder) -> {
Fetch fetch = rootA.fetch(A_.listOfB, JoinType.LEFT);
ListJoin listJoin = ((ListJoin)fetch).join(B_.listOfC)
return builder.equal(listJoin.get(C_.xyz), xyz);
}
I am reusing the implicit join done by the fetch operation. This join is not working in specification. It's outputting the following JPQL.
SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
WHERE c.xyz = :xyz
Saying that, there is no c alias.
I have looked into Hibernate GitHub source code. I found out that, there is a class named QueryStructure.java responsible for generating JPQL query from the criteria object.
I found the function renderFetches which render the fetches.
#SuppressWarnings({ "unchecked" })
private void renderFetches(
StringBuilder jpaqlQuery,
RenderingContext renderingContext,
Collection<? extends Fetch> fetches) {
if ( fetches == null ) {
return;
}
for ( Fetch fetch : fetches ) {
( (FromImplementor) fetch ).prepareAlias( renderingContext );
jpaqlQuery.append( renderJoinType( fetch.getJoinType() ) )
.append( "fetch " )
.append( ( (FromImplementor) fetch ).renderTableExpression( renderingContext ) );
renderFetches( jpaqlQuery, renderingContext, fetch.getFetches() );
}
}
Similarly there is a function renderJoins responsible for all the joins.
These two are recursive functions rendering the criteria object tree.
It's clear that all the joins inside the fetches are ignored. There is no call to function renderJoins inside from renderFetches which causes the generated query to be incomplete.
Is there any in depth reason why we are not joining inside from a fetch? If yes then how could I reuse the existing implicit joins done by fetch?
This issue is re-generated using hibernate test case template.
Regenerated Hibernate test case template
Specific test case file
As i mentioned in my question, there is no renderJoins call from inside of renderFetches, adding following at the end of the renderFetches solves the issue.
if (fetch instanceof From) {
From from = (From) fetch;
renderJoins(jpaqlQuery, renderingContext, from.getJoins());
}
I have given a PR HHH-14916 in the hibernate-orm.
PR is merged will be available in the next 5.6.x release.
I have a two Tables in Maximo 7.5.
Table A: has attribute WORKORDERNUM, EXPECTEDTIME and FINISHTIME in table A.
Table B: has attribute WORKNUM and STATUS in table B.
What I want to do is:
if FINISHTIME > EXPECTEDTIME then update STATUS in table B as "NOTGOOD" otherwise do nothing.
I have created a CronTask for that which will be running every five minutes.
Now I can think of is two approaches.
1. To loop through all Table A. Inside the loop perform a database query for Table B each time.
Here is the sample code:
MboSetRemote TableA = mxs.getMboSet("TABLEA", ui);
MboSetRemote TableB = mxs.getMboSet("TABLEB", ui);
TableA.setWhere("FINISHTIME > EXPECTEDTIME");
TableA.reset();
TableB.setWhere("");
TableB.reset();
MboSet TableARow = null;
MboSet TableBRow = null;
//now it will give a list of entries. Which needs to be matched with Table B and values be updated in Table B STATUS.
while ((TableARow = TableA.getMbo(i)) != null)
{
int A = TableA.getString("WONUM");
while((TableBRow = TableB.getMbo(i)) != null)
int B = TableB.getString("WONUM");
if (A == B){
//set SATUS etc}
}
TableB.save();
TableA.save();
2. To loop through all Table A. Inside the loop perform Compare the values for Table B each time.
MboSetRemote TableA = mxs.getMboSet("TABLEA", ui);
MboSetRemote TableB = mxs.getMboSet("TABLEB", ui);
TableA.setWhere("FINISHTIME > EXPECTEDTIME");
TableA.reset();
MboSet TableARow = null;
//now it will give a list of entries. Which needs to be matched with Table B and values be updated in Table B STATUS.
while ((TableARow = TableA.getMbo(i)) != null)
{
TableB.setWhere("WONUM= TABLEA.WONUM");
TableB.reset();
//set SATUS etc
TableB.save();
}
TableA.save();
Which one is the better and more cost effective?
Any other suggestions?
Automation scripts are fun to write and use, but they aren't always the best tool for the job. In this case, I would use
an Escalation to search TableA for FINISHTIME > EXPECTEDTIME
a Relationship in Database Configuration from TableA to TableB where wonum = :wonum and siteid = :siteid
an Action based on TableA that uses the above Relationship in standard notation to set the status in TableB
The main benefits of this approach over the ones presented are upgradeability and supportability. Upgradeability, because no code is involved that can be deprecated and because all configurations are upgradeable, and supportability because IBM supports configurations but not customizations. (In the case of Automation Scripts, your ability to write them is supported, but your code, itself, is not. It is the same with Relationships in Database Configuration.)
The most cost effective thing to do here would not be to use a Crontask every 5 minutes, but to perform a check just when every record is changed. This would be much much more efficient.
Make two custom field classes, one for each of the date fields, and attach the to the fields on database configuration.
The one on EXPECTEDTIME should be something like this:
public class CustFldExpectedTime extends MboValueAdapter {
public CustFldExpectedTime(MboValue mbv) throws MXException {
super(mbv);
}
#Override
public void action() throws MXException, RemoteException {
super.action();
MboValue mv = getMboValue();
MboRemote mbo = mv.getMbo();
Date expectedTime = mv.getDate();
Date finishTime = mbo.getDate("FINISHTIME");
if(finishTime.after(expectedTime)) {
// first argument is the name of on-the-fly relationship
// second argument is the name of the table your relationship is pointing to
// third argument is the relationship where clause
MboSetRemote tableBSet = mbo.getMboSet("$TABLEB", "TABLEB", ":WORKORDERNUM = WORKNUM");
if(!tableBSet.isEmpty()) {
MboRemote tableB = tableBSet.getMbo(0);
tableB.setValue("STATUS", "NOTGOOD");
}
}
}
}
I have a model:
#Entity
class A {
#ManyToOne
B b;
int i;
}
#Entity
class B {
#OneToMany(mappedBy="b")
List< A > list = new ArrayList< A >();
}
Now in my EJB service I do:
void f(Aid,Bid){
A a = em.find(A.class, Aid);
B b = em.find(B.class, Bid);
a.setB(b);
}
This throw db constraint exception (INSERT INTO B VALUES... WITH SAME VALUES THAT ALREADY EXIST IN CURRENT B).
Why it's try to insert a new record to B while it's already exist in B table and the relationship is not PERSIST?
NOTES:
1 When i do after the last row:
em.merge(a);
It's work as well...???
2 When i do a.setI(1); without merge - it's changed in DB (like i thought it will)
THANKS!!!
It's a bidirectional relationship. You should always update both sides of the relations ships. Otherwise, if you don't directly have problems while persisting, you can easily corrupt your cache and face some stange bugs in your application.
So, if you have this:
A a = em.find(A.class, Aid);
B b = em.find(B.class, Bid);
a.setB(b);
You should add this right after:
List<A> aList = b.getList();
if (!aList.contains(b)) {
aList.add(b);
}
2 When i do a.setI(1); without merge -
it's changed in DB (like i thought it
will)
If you're using an entityManager in an ejb (which I seems you do regarding your tags), it's normal that changes to managed entities are persisted. Was that te question?
Also, you should make sure that entities identity, equals() and hashcode() are defined correctly (maybe it is, but you didn't mention it).
I'm converting a legacy iBatis implementation to Hibernate, and for backwards compatibility purposes need to present counts of an object's collections rather than the collections themselves. The original query was:
select A.*, ( select count(*) from B where B.A_id = A.id ) as B_count from A;
and b_count would be presented in the response. I'd like to be able to do the same without lazy-loading A's collection of B's for each query result.
Any ideas or suggestions?
The best method seems to be using a Hibernate formula, mapped to the getter and setter of my BCount attribute in the class A. My code:
public class A {
// ...
private long bCount;
// ...
#Formula( "(select count(*) from B where B.A_id = id" )
public long getBCount() {
return this.bCount;
}
public void setBCount( long bCount ) {
this.bCount = bCount;
}
}
Great thing about this method is that the count is returned in the same fetch to hydrate the initial object, and does not result in 1+N queries for collection query results!
You can use a projection.
The syntax for the row count is below:
Criteria crit = session.createCriteria(B.class);
crit.setProjection(Projections.rowCount());
List results = crit.list();
Edit: After re-reading, I think this may not be what you're asking for....
Hibernate filters are used to apply additional restrictions to query results (e.g. think of them as part of "where" clause), so they won't do what you want. You have two options here:
A) You can eagerly get collection of Bs for your A:
from A a left join fetch a.Bs b
If you do so, keep in mind that for queries that would return multiple As you may get duplicates in the result list (e.g. if you have 2 As and each of them has 3 Bs you'll get 6 results back). Wrap them in a set to ensure uniqueness.
B) Assuming you have an appropriate constructor for A, you can do the following:
select new A(a.field1, a.field2, ... a.fieldN, count(*) as B_count)
from A a left join a.Bs b
group by a.field1, a.field2, ... a.fieldN
I wonder how JPA defines to handle following scenario:
Table A: | Table B:
ID FK_B | ID
1 10 | 10
2 null | 12
3 11 |
I want all Table A entries with FK_B NULL or referencing not available Table B entry.
public class A implements Serializable {
#Id
private Long id;
#JoinColumn(name = "FK_B", nullable = true)
#ManyToOne
private B b;
}
public class B implements Serializable {
#Id
private Long id;
}
Is it defined, what happens if I use
SELECT a FROM A a LEFT JOIN a.b WHERE a.b IS NULL
or: (Is this possible?)
SELECT a FROM A a LEFT JOIN B b on (b = a.b) WHERE b IS NULL
What I need is a list containing
A(id = 2)
A(id = 3)
Thanks a lot!
Row #3 in your Table A is illegal by definition; if there's no B with ID=11 you can't have that row in table A for you'd be violating the foreign key constraint.
As far as getting all rows from A where B is null, your first query should work. You can also try:
SELECT a FROM A a WHERE a.b.id IS NULL
although I'm not 100% sure whether that's valid JPA QL syntax (it works for Hibernate)