Envers #ManyToMany subquery - subquery

I have an audited entity A. Entity A holds field 'name' and a collection of entities B (annotated as Many-to-many relationship). I created an instance of A, defined name, collection of entities B and save all it into DB. This is revision #1. Then I changed name of A and update it in DB. This is revision #2.
I use the following method to get all entities of class A at revision #2
List<A> list = getAuditReader().createQuery().forEntitiesAtRevision(A.class, 2)
.add(AuditEntity.revisionNumber().eq((int) revisionId)).getResultList();
I get entity A at revision #2, but Envers also fetches collection of entities B related to this A from revision #1. Here an example of query used by Envers:
SELECT a_b_aud.a_id, a_b_aud.b_id
FROM a_b_aud CROSS JOIN b_aud
WHERE a_b_aud.b_id=b_aud.id
AND b_aud.rev=(SELECT max(b_aud2.rev)) FROM b_aud AS b_aud2 WHERE b_aud2.rev<=2 AND b_aud.id=b_aud2.id)
AND a_b_aud.rev=(SELECT max(a_b_aud2.rev)) FROM a_b_aud AS a_b_aud2 WHERE a_b_aud2.rev<=2 AND a_b_aud.a_id=a_b_aud2.a_id AND a_b_aud.b_id=a_b_aud2.b_id)
But actually I need NULL as a collection of entities B in case of there were no changes for it at revision #2 (because of performance issue).
There are two subselects in this query. And if we have more then one collection of entities related to A (C, D, E, F) and for about 100 thousands rows for each b_aud and a_b_aud the query above takes a lot of time.
I defined entity B as not audited (i.e. did not add #Audited annotation into B) and defined A B relation by the following:
#ManyToMany
#Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
#JoinTable(name = "a_b", joinColumns = #JoinColumn(name = a_id))
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
public Set<B> getBs();
It fixes first SUBSELECT.
But I can not find standard solution to not query B's if it do not exist for requested revision (in my case #2). So the query should look like:
SELECT a_b_aud.a_id, a_b_aud.b_id
FROM a_b_aud CROSS JOIN b_aud
WHERE a_b_aud.b_id=b_aud.id b_aud.rev=2 AND a_b_aud.rev=2
The only solution I found is using native sql query and to execute it using hibernate template. Then convert result values into entity A using ResultTransformer.
Could anybody help with this issue? Is there a standard configuration/annotation I need to add to avoid second SUBSELECT?

There's no option in Envers not to load related entities when requested. Not however, that the B entities are always loaded lazily (regardless of the annotations on the relation), so if you don't want to execute the query which loads them, simply do not access that field.
If you want better read performance, you may also want to look at the validity audit strategy, see http://docs.jboss.org/hibernate/core/4.1/devguide/en-US/html/ch15.html#d5e4085. It has faster reads, but slower writes.

Related

django remove m2m instance when there are no more relations

In case we had the model:
class Publication(models.Model):
title = models.CharField(max_length=30)
class Article(models.Model):
publications = models.ManyToManyField(Publication)
According to: https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_many/, to create an object we must have both objects saved before we can create the relation:
p1 = Publication(title='The Python Journal')
p1.save()
a1 = Article(headline='Django lets you build web apps easily')
a1.save()
a1.publications.add(p1)
Now, if we called delete in either of those objects the object would be removed from the DB along with the relation between both objects. Up until this point I understand.
But is there any way of doing that, if an Article is removed, then, all the Publications that are not related to any Article will be deleted from the DB too? Or the only way to achieve that is to query first all the Articles and then iterate through them like:
to_delete = []
qset = a1.publications.all()
for publication in qset:
if publication.article_set.count() == 1:
to_delete(publication.id)
a1.delete()
Publications.filter(id__in=to_delete).delete()
But this has lots of problems, specially a concurrency one, since it might be that a publication gets used by another article between the call to .count() and publication.delete().
Is there any way of doing this automatically, like doing a "conditional" on_delete=models.CASCADE when creating the model or something?
Thanks!
I tried with #Ersain answer:
a1.publications.annotate(article_count=Count('article_set')).filter(article_count=1).delete()
Couldn't make it work. First of all, I couldn't find the article_set variable in the relationship.
django.core.exceptions.FieldError: Cannot resolve keyword 'article_set' into field. Choices are: article, id, title
And then, running the count filter on the QuerySet after filtering by article returned ALL the tags from the article, instead of just the ones with article_count=1. So finally this is the code that I managed to make it work with:
Publication.objects.annotate(article_count=Count('article')).filter(article_count=1).filter(article=a1).delete()
Definetly I'm not an expert, not sure if this is the best approach nor if it is really time expensive, so I'm open to suggestions. But as of now it's the only solution I found to perform this operation atomically.
You can remove the related objects using this query:
a1.publications.annotate(article_count=Count('article_set')).filter(article_count=1).delete()
annotate creates a temporary field for the queryset (alias field) which aggregates a number of related Article objects for each instance in the queryset of Publication objects, using Count function. Count is a built-in aggregation function in any SQL, which returns the number of rows from a query (a number of related instances in this case). Then, we filter out those results where article_count equals 1 and remove them.

Maximo automatisation script to change statut of workorder

I have created a non-persistent attribute in my WoActivity table named VDS_COMPLETE. it is a bool that get changed by a checkbox in one of my application.
I am trying to make a automatisation script in Python to change the status of every task a work order that have been check when I save the WorkOrder.
I don't know why it isn't working but I'm pretty sure I'm close to the answer...
Do you have an idea why it isn't working? I know that I have code in comments, I have done a few experimentations...
from psdi.mbo import MboConstants
from psdi.server import MXServer
mxServer = MXServer.getMXServer()
userInfo = mxServer.getUserInfo(user)
mboSet = mxServer.getMboSet("WORKORDER")
#where1 = "wonum = :wonum"
#mboSet .setWhere(where1)
#mboSet.reset()
workorderSet = mboSet.getMbo(0).getMboSet("WOACTIVITY", "STATUS NOT IN ('FERME' , 'ANNULE' , 'COMPLETE' , 'ATTDOC')")
#where2 = "STATUS NOT IN ('FERME' , 'ANNULE' , 'COMPLETE' , 'ATTDOC')"
#workorderSet.setWhere(where2)
if workorderSet.count() > 0:
for x in range(0,workorderSet.count()):
if workorderSet.getString("VDS_COMPLETE") == 1:
workorder = workorderSet.getMbo(x)
workorder.changeStatus("COMPLETE",MXServer.getMXServer().getDate(), u"Script d'automatisation", MboConstants.NOACCESSCHECK)
workorderSet.save()
workorderSet.close()
It looks like your two biggest mistakes here are 1. trying to get your boolean field (VDS_COMPLETE) off the set (meaning off of the collection of records, like the whole table) instead of off of the MBO (meaning an actual record, one entry in the table) and 2. getting your set of data fresh from the database (via that MXServer call) which means using the previously saved data instead of getting your data set from the screen where the pending changes have actually been made (and remember that non-persistent fields do not get saved to the database).
There are some other problems with this script too, like your use of "count()" in your for loop (or even more than once at all) which is an expensive operation, and the way you are currently (though this may be a result of your debugging) not filtering the work order set before grabbing the first work order (meaning you get a random work order from the table) and then doing a dynamic relationship off of that record (instead of using a normal relationship or skipping the relationship altogether and using just a "where" clause), even though that relationship likely already exists.
Here is a Stack Overflow describing in more detail about relationships and "where" clauses in Maximo: Describe relationship in maximo 7.5
This question also has some more information about getting data from the screen versus new from the database: Adding a new row to another table using java in Maximo

Is that possible using queryDSL?

There are two entities A and B relate one to many.
I want to extract "B_2" with there parent("A_1") and children("B_1", "B_2", "B_3") of there parent. So I try my code below.
from(QB.b).join(QB.b.a, QA.a).fetch().join(QA.a.bList).fetch().where(QB.b.name.eq("B_2"));
But result B.a.bList are just proxy object. And it occur to N+1 problem.
Anyone help?
For the purposes of this example, let's called the Querydsl query types for entity A a and for entity B b.
.from(a)
.innerJoin(b)
.on(a.a_id).eq(b.a_id)
.where(b.name.eq("B_2"))
.fetch();

Restkit: when a foreign key is set to null the relationship in Core Data is not reset

Basically when a foreign key becomes null (after it was set to a value) the relationship in core data is not reset.
Take as an example the following one-to-many relationship:
contact <<---> company (contact has one company, company has many contacts)
Which is mapped in both directions with the following methods from Restkit:
RKRelationshipMapping *contactCustomerRelationshipMapping = [RKRelationshipMapping relationshipMappingFromKeyPath:#"contacts" toKeyPath:#"hasContacts" withMapping:contactResponseMapping];
[customerResponseMapping addPropertyMapping:contactCustomerRelationshipMapping];
[contactResponseMapping addConnectionForRelationship:#"forCustomer" connectedBy:#{#"companyID" : #"identifier"}];
Then, assume that a contact is linked to a company both in core data and in the remote server, so the JSON returns:
company_id = 123
which is correctly mapped to the relationship in Core Data.
Although when the relationship is null-ed out the returning JSON in response of a GET contact returns:
'contact': {
....
address = "20 Wordworth Ave";
city = "<null>";
"company_id" = "<null>";
...
}
The company_id is then set correctly in the core data entity but the relationship connection mapper then does not delete the reference to the company with id 123 via the relationship. So it seems like Restkit is not applying the null value of the foreign key to the relationship in Core Data.
I have verified that this happens only when company_id is reset to null and not when the value is changed to another company_id.
Let me know if you have any suggestion on how to solve the issue.
(Right now I am thinking to implement the setter for company_id and manually reset the relationship when it's null)
Thanks a lot!
I am using the latest Restkit development branch (which is tagged as 0.21.0 - currently the lastest release is 0.20.3 but blake watters told me that the development branch has already been tagged but he did not have the time to prepare docs)
I am actually using cocoapods and included the latest dev release with the line:
pod 'RestKit', :head
Your workaround should be doable.
This could be classed as a bug in RestKit. As such you'd be better off raising it as an issue. You can also looking at adding it as a feature.
It's possible that you could use fetchRequestBlocks in order to provide RestKit with the information required to handle this, but this would result in the object being deleted which may not be what you want.

Unable to update the EntitySet (no insert function)

I have this tables in my data base:
Videos(IDVideo, Name...)
Episodes(IDEpisode, Name...)
Versions(IDVersion, Name, ....)
VideosVersions(IDVideo, IDEpisode, IDVersion)
A version can be for a video or for the episode and the relation in both cases is N:N, a video can have many versions and a version can be asigned to many videos or episodes.
In my edmx model has the four tables.
When I try to assign a new version to a video for example I use this code:
VideosVersions myVideoVersion = new VideosVersions();
myVideoVersion.IDVideo = paramVideo.IDVideo;
myVideoVersion.IDVersion = paramVersion.IDVersion;
myContext.VideosVersions.Add(myVideosVersions);
myContext.SaveChanges();
But I get the following exception:
Unable to update the EntitySet 'VideosVersions' because it has a DefiningQuery and no <InsertFunction> element exists in the <ModificationFunctionMapping> element to support the current operation.
Why?
Thanks.
EDIT: in my entity in the edmx model the three properites has a key, so if I am not wrong, this means that the PK of the entity is the combination of the three fields, is correct?
The mapper probably couldn't figure out which columns should be the primary key of VideosVersions. This means it doesn't know how to insert new records and only provides the query method. Make sure you have the primary key properly defined on the table, then update the mapping.

Resources