-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Referencing specific revision in mapping using envers
PostPosted: Mon Oct 03, 2016 5:40 pm 
Newbie

Joined: Mon Oct 03, 2016 5:17 pm
Posts: 2
Hi,

I recently found out about envers for auditing and have been able to use it successfully to track revisions & fetch them using the @Audited annotation & the AuditReader. Now, what I'm trying to achieve is retaining the mappings to an audited entity at the revision they were made instead of the newest revision.

Quick example:

Let's say I have a recipe for cookies which I use to make batches of cookies (pseudo classes for classes below). Each recipe has a list of instructions to follow and doing so creates a batch of cookies:

Code:
@Audited
class CookieRecipe {
    @OneToMany(mappedBy="recipe")
    private List<RecipeStep> steps;

    private void addStep(String instruction) {
        steps.add(new RecipeStep(instruction));
    }
}

class CookieBatch {
    @ManyToOne
    @JoinColumn(...)
    private CookieRecipe recipe;
}

@Audited
@Table(name="recipe_step")
class RecipeStep {

    @Column
    private String instruction;
   
    @ManyToOne
    @JoinColumn(...)
    private CookieRecipe recipe;

    private RecipeStep(String instruction) {
        this.instruction = instruction;
    }
}



Now, let's say I have this Cookie Recipe:

Code:
CookieRecipe recipe = new CookieRecipe();
recipe.addStep("Make the dough");
recipe.addStep("Place on pan");
recipe.addStep("Bake at 400F for 20 minutes");
entityManager.persist(recipe);



And I'll be using this recipe to create my first batch of cookies:
Code:
CookieBatch batch = new CookieBatch(recipe);
entityManager.persist(batch);



Now if I wanted to change the recipe to say, for example, 375F instead of 400F, this creates revision 2 of the CookieRecipe, which is what I expect and want. However, I want the batch I already created to point to revision 1 of the CookieRecipe. Currently, if I fetch the CookieBatch I've already created using its ID, the reference to the CookieRecipe ends up being the latest revision (the one with 375F). Is this something I can accomplish using envers?


Top
 Profile  
 
 Post subject: Re: Referencing specific revision in mapping using envers
PostPosted: Tue Oct 04, 2016 8:47 am 
Hibernate Team
Hibernate Team

Joined: Wed Jun 15, 2016 9:04 am
Posts: 24
What you're going to need here is to introduce a second bidirectional relationship between CookieRecipe and CookieBatch. You have a unidirectional relationship, but making this bidirectional will allow Envers to properly filter the right versions.

So first modify CookieRecipe as below:
Code:
@Audited
@Entity
public class CookieRecipe {
  @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
  private List<CookieRecipeStep> steps = new ArrayList<>();
  @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
  private List<CookieBatch> batches = new ArrayList<>();
}


When you modify any element in either steps or batches, this will trigger a revision on the parent entity, in this case CookieRecipe. So the following code should fetch the appropriate revisions for you:

Code:
AuditReader reader = AuditReaderFactory.get( entityManager );
for ( Number revision : reader.getRevisions( CookieRecipe.class, recipe.getId() ) ) {
  CookieRecipe recipe = reader.find( CookieRecipe.class, recipe.getId(), revision );
  // recipe.getBatches() will contain all batches with a revision number less-than or equal-to revision
  // recipe.getSteps() will contain all steps with a revision number less-than or equal-to revision
}


Let me know if you have any problems.


Top
 Profile  
 
 Post subject: Re: Referencing specific revision in mapping using envers
PostPosted: Wed Oct 05, 2016 12:36 pm 
Newbie

Joined: Mon Oct 03, 2016 5:17 pm
Posts: 2
Ah, I see you found both my questions! Thanks for your help :)

My goal is to be able to fetch the CookieBatch using the CookieBatch's ID instead of searching for a CookieRecipe and getting the batches that were created.

Example might be: I had a cookie form a batch and they tasted burnt. I want to view the recipe followed for that batch and see what instructions were followed. I'd wanna do a

Code:
entityManager.find(CookieBatch.class, id)


and view the instructions. Or
Code:
AuditReader reader = AuditReaderFactory.get( entityManager );
for ( Number revision : reader.getRevisions( CookieBatch.class, batchId) ) {
  CookieBatch batch = reader.find( CookieBatch.class, batchId, revision);
  // view recipe
}


Top
 Profile  
 
 Post subject: Re: Referencing specific revision in mapping using envers
PostPosted: Fri Oct 07, 2016 1:04 pm 
Hibernate Team
Hibernate Team

Joined: Wed Jun 15, 2016 9:04 am
Posts: 24
I view a batch as an immutable row, so for each time a recipe is prepared, whether its altered or not, a new batch row should be inserted.

Code:
// Revision 1 - Create Recipe
entityManager.getTransaction().begin();
Recipe recipe = new Recipe();
recipe.addStep( "step1" );
recipe.addStep( "step2" );
entityManager.persist( recipe );
entityManager.getTransaction().commit();

// Revision 2 - Create batch #1 for the current recipe
entityManager.getTransaction().begin();
Batch batch1 = new Batch();
batch1.setRecipe( recipe );
entityManager.persist( batch1 );
entityManager.getTransaction().commit();

// Revision 3 - Create batch #2 with a modified recipe
entityManager.getTransaction().begin();
recipe.getSteps().get( 1 ).setInstruction( "step2-modified" );
entityManager.merge( recipe );
Batch batch2 = new Batch();
batch2.setRecipe( recipe );
entityManager.persist( batch2 );
entityManager.getTransaction().commit();

// clear session cache
entityManager.clear();

// Iterate revision data
AuditReader reader = AuditReaderFactory.get( entityManager );
for ( Batch batch : Arrays.asList( batch1, batch2 ) ) {
  for ( Number revision : reader.getRevisions( Batch.class, batch.getId() );
    Batch revision = reader.find( Batch.class, batch.getId(), revision );
    // display revision.getRecipe()
  }
}


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.