Hibernate Envers-Get all entities, revision numbers, revision dates and revision types of an Entity by its ID - hibernate-envers

Using Hibernate Envers I want to get all entities, revision numbers, revision dates and revision types of an Entity by its ID.
Currently I am doing this to obtain the entity, revision number and revision date:
public List<Alumno> obtenerAuditoriaAlumno(Long idAlumno) {
AuditReader auditReader = AuditReaderFactory.get(entityManager);
List<Number> revisionNumbers = auditReader.getRevisions(Alumno.class, idAlumno);
List<Alumno> auditoriaAlumno = new ArrayList<Alumno>();
for (Number rev : revisionNumbers) {
Alumno alumno = auditReader.find(Alumno.class, idAlumno, rev);
Date revisionDate = auditReader.getRevisionDate(rev);
alumno.setRevisionNumber(rev.intValue());
//alumno.setRevisionType(revisionType); // GET THIS
alumno.setRevisionDate(revisionDate);
auditoriaAlumno.add(alumno);
}
return auditoriaAlumno;
}
Is it possible to obtain it with one query?
Should I add these fields directly to the Entity?

I would suggest you take a look at using forRevisionsOfEntity. You access this method by using the AuditReader interface as follows:
auditReader.createQuery().forRevisionsOfEntity(
YourAuditEntityClass.class,
false, // false returns an array of entity and audit data
true // selects the deleted audit rows
);
The important method argument here is the second argument as that influences the returned Object type. When its true, you'll be returned the actual audited entity instances for each revision; however, when its false you'll be returned an Object[] array of values of which are:
The entity instance.
The revision entity instance (where you can get the revision number and date)
The revision type, e.g. ADD, MOD, DEL.
HTH.

Related

Hibernate Envers Audit Query- retrieve only the most recent revision for all entities

I would like to retrieve the all recent versions of all entities( i.e. everything in Database) that has changed recently.
Following query fetch revisions of specific entity "MyEntity"
queryObject = auditReader.createQuery().forRevisionsOfEntity(MyEntity.class, false, true).addOrder(AuditEntity.revisionNumber().desc())
But I need a mechanism to fetch records for all entities irrespective of particular entity type.
Look into org.hibernate.envers.track_entities_changed_in_revision.
This setting causes a join-table to be created against the revision-entity where a string-based set of entity names will be tracked for each revision number. With this information, you should be able to build necessary queries using the AuditReader#createQuery() API to iterate all the changes.
In pseudo code, it would be something like:
List<Number> revisions = // create AuditQuery to get all revision numbers
for ( Number revision : revisions ) {
DefaultTrackingModifiedEntitiesRevisionEntity revisionEntity = // create query to get revision entity
for ( String entityName : revisionEntity.getModifiedEntityNames() ) {
// create query based on entityName + revisionNumber
}
}

Entity Framework TPH - Additional WHERE clause only for one subtype

Suppose I have a class Parent, with two subclasses, AChild and BChild. I have these mapped to a single table using Entity Framework 5.0.0 on .NET 4.5, using TPH.
public abstract class Parent {
public string Type { get; set; } // Column with this name in DB is discriminator.
public string Status { get; set; }
}
public class AChild : Parent {
// Other stuff.
}
public class BChild : Parent {
// Other stuff.
}
The code to configure the mapping:
class ParentConfiguration : EntityTypeConfiguration<Parent> {
Map((EntityMappingConfiguration<AChild> mapping) => mapping
.Requires("Type")
.HasValue("A"));
Map((EntityMappingConfiguration<BChild> mapping) => mapping
.Requires("Type")
.HasValue("B"));
}
I have a need to run a query that returns both AChild and BChild objects. However, it needs to filter ONLY the AChild rows by a second column, which in this example I will call Status.
Ideally I would want to do the following:
public IList<Parent> RunQuery() {
IQueryable<Parent> query =
this.context.Set<Parent>()
.Where((Parent parent) => !parent.Type.Equals("A") || parent.Status.Equals("Foo"))
.OrderBy((Parent parent) => parent.Number);
return query.ToList();
}
This doesn't work. It insisted on looking for a "Type1" column instead of just letting both the discriminator and a "Type" property be mapped to the same "Type" column.
I know of the "OfType" extension method that can be used to completely filter down to one type, but that's too broad a brush in this case.
I could possibly run multiple queries and combine the results, but the actual system I'm building is doing paging, so if I need to pull back 10 rows, it gets messy (and inefficient) to query since I'll either end up pulling back too many rows, or not pull back enough and have to run extra queries.
Does anyone have any other thoughts?
There are few problems. First of all you cannot have discriminator mapped as a property. That is the reason why it is looking for Type1 column - your Type property results in second column because the first one is already mapped to .NET types of your classes. The only way to filter derived types is through OfType.
The query you want to build will be probably quite complex because you need to query for all Bs and concatenate them with result of query for filtered As. It will most probably not allow you to concatenate instances of Bs with As so you will have to convert them back to parent type.

Hibernate Envers : track revisions in the owning side of a OneToMany relation

I have two audited entities, A and B. Entity A holds a collection of entity B (annotated as One-to-many relationship). When inserting a new instance of A into the database, all rows of A and B are at the same revision (let's say revision 1). Then, there is an update on A which only affect the instances of entity B (cascade type is merge). So after the update, the entity A is still at revision 1, whereas the entities of B are at revision 2 (new MOD entry in the audit table).
The problem is when I retrieve all the revisions of A, I would expect to get 2 revisions in return : one for the creation, one for the modification of the owning collection of B.
I can get this behaviour in case of ManyToMany but I can't get it work the same way with a OneToMany relation.
(I'm using Hibernate 3.6.10-Final)
I solved my problem by adding a hidden lastUpdated date field on my equivalent of your A entity.
#Entity
public class A {
private Date lastModified;
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL )
private List<B> blist;
public void touch(){
lastModified=new Date();
}
}
In the related entities (like you B field), I added the following :
public class B {
#ManyToOne
private A a;
#PreUpdate
public void ensureParentUpdated(){
if(a!=null){
a.touch();
}
}
}
This ensures that a revision is added to A whenever a revision is added to B.

Entity Framework 4.1 Code First - auto increment field on insert for non primary key

My model contains an Order (parent object) and Shipments (child object). The database table for these already have a surrogate key as an auto-increment primary key.
I have the business rule is that for each shipment in the order, we need to have an auto generated "counter" field -- e.g. Shipment 1, Shipment 2, Shipment 3, etc. Shipment model has properties: "ShipmentId", "OrderId", "ShipmentNumber". My attempted implemention is to have ShipmentNumber an int and in code(as opposed to database), query the Shipment collection and do max() + 1.
Here's a code snipet of what I'm doing.
Shipment newShipmentObj = // blah;
int? currentMaxId = myOrderObj.Shipments
.Select(x => (int?) x.ShipmentNumber)
.Max();
if (currentMaxId.HasValue)
newShipmentObj.ShipmentNumber = currentMaxId.Value + 1;
else
newShipmentObj.ShipmentNumber = 1; // 1st one
myOrderObj.Shipments.Add(newShipmentObj);
// etc.. rest of EF4 code
Is there a better way?
I don't really like this as I have the following problems because of potential transaction/concurrency issues.
My Order object also has a autoincrement "counter" -- e.g. Order 1, Order 2, Order 3, ... My Order model has properties: "OrderId", "CustomerId", "OrderNumber".
My design is that I have an OrderRepository but not a ShipmentRepository. The ShipmentRepository could query off the Order.Shipment collection... but with Orders, I have to query directly off the dbcontext, e.g.
int? currentMaxId = (_myDbContext)).Orders
.Where(x => x.CustomerId == 123456)
.Select(x => (int?)x.OrderNumber)
.Max();
However, the above part doesn't work well if I attempt to add multiple objects to the DbContext without committing/saving changes to the database. (i.e. the .Where() returns null... and only works if I use DbContext ".Local", which is not what I want.)
Help! Not sure what the best solution would be. Thanks!
you seem to already have shipmentid that is incremental. you can use it for you shipment number and maybe combined with current date as described here: How to implement gapless, user-friendly IDs in NHibernate? what you are trying to do with Max() is evil. Stay away from it as it can cause problems with getting the same shipment numbers for multiple shipments when the load is high

EF4 inheritance and Stored procedures

I implemented inheritance with a discriminator field so all my records are in the same table. My basetype is Person (also the name of the table) and Driver and Passenger inherit from it. I receive instances of the correct type (Driver and Passenger) when I perform a query on the object context to Person. example:
var q = from d in ctx.Person
select d;
But I also create a function that calls a stored procedure and mapped the output of the function to the type Person. But now I get a list of Person and not Drivers or Passengers when I execute this method.
Anybody an idea how to solve this or is this a bug in EF4?
AFAIK, you can't use discriminator mapping (e.g TPH) when dealing with stored procedure mappings.
The stored procedure must be mapped to a complex type or custom entity (e.g POCO), the mapping cannot be conditional.
What you could do is map it to a regular POCO, but then project that result set into the relevant derived type (manual discrimination).
E.g:
public ICollection<Person> GetPeople()
{
var results = ExecuteFunction<Person>(); // result is ObjectResult<Person>
ICollection<Person> people = new List<Person>();
foreach (var result in results)
{
if (result.FieldWhichIsYourDiscriminator == discriminatorForDriver)
{
people.Add((Driver)result);
}
// other discriminators
}
}
If your always expecting a collection of one type (e.g only Drivers), then you wouldn't need the foreach loop, you could just add the range. The above is in case you are expecting a mixed bag of different people types.
Would be interested to see other answers, and if there is a better way though - but the above should work.