Database design to track changes - w/ Hibernate - java

Hey All I'm having a difficult time with a database design. As you can see from my current design a Registration can have multiple EmployerRegistrations which can have multiple ClientRegistrations. It's pretty simple from here. A new registration needs to be created by the user each year.
Unfortunately I need to be able to track changes / amendments. Changes can be made to the Registration information (name, address, etc.) or Client Registration information (remove / add client or remove / add employer).
I've tried a bunch different designs, but so far nothing feels "right". Tracking amendments in the Registration table is easy as this affects all tables above it. All id's are updated. It's the changes to the ClientRegistration table that's throwing me for a loop. As you can see I have a version column I was trying, but it's not helping me much. With Hibernate it feels like each amended client registration needs it's own unique registration object, but creating a whole new registration for any client registration amendments doesn't seem right / efficient.
I've been battling this for about a week so any help would be greatly appreciated. Thanks!

Have you checked out Hibernate Envers? Its an automatic versioning plugin for Hibernate. Makes tracking history of object changes very easy. It is configured in an AOP way, so you can simply annotate the object you want audited and let Envers handle the details:
#Entity
#Audited
public class Person {
#Id
#GeneratedValue
private int id;
private String name;
private String surname;
#ManyToOne
private Address address;
...
}

You can use Envers, which is now bundled in hibernate-core. Check the docs

Have you come across the concept of Temporal data modeling? You might want to Google for it. One very popular technique for temporal modeling is "Effective Dated" logic, used extensively in Peoplesoft. Briefly, it goes like this:
Every table in the system would have this design pattern:
Table{
Primary_key,
effdt,
effseq,
other data,
modified_ts
};
Multiple versions of the record are "stacked up" using the primary key, effdt and effseq. Effdt stores date only, not datetime. effseq (int) is used store multiple changes on the same day. modified_ts stores the date stamp of data change.
the data in a table would look like this:
PrimaryKey1 2012-01-01 1 MyData1 MyData2
PrimaryKey1 2012-02-01 1 MyData1 Change1
PrimaryKey1 2012-02-01 2 Change2 Change1
To get the latest data from any table, you would use the query like this:
select * from MyTable A
where effdt = (select max(effdt) from MyTable where PrimaryKey = A.PrimaryKey)
and effseq = (select max(effseq) from MyTable where PrimaryKey = A.PrimaryKey
and Effdt=A.EFfdt)
Will that help?

Related

Weblogic to Liberty w JPA upgrade - related entities intermittently not being queried

just a quick question please in case something stands out immediately.
We're migrating an EAR/EJB application from Weblogic 11g to latest WS Liberty (22.x) also upgrading several of the frameworks including JPA to 2.2. This also changes JPA implementation to eclipseLink. We came from com.oracle.weblogic.11g.modules:javax.persistence:1.0.0.0_1-0-2. Underlying DB is MS-SQL Server.
And I'm running into some weirdness with regards to related objects not being resolved/queried intermittently.
Just as an example we have entities where the columns hold reference data codes or similar lookups. Say I have an entity called PayemntRecordT and it has a status code which refers to a ref table that also holds a textual description. Something like this:
SQL:
CREATE TABLE [PAYMENT_RECORD_T](
[PAYMENT_ID] [int] NOT NULL,
...
[PAYMENT_STATUS_CD] [CHAR](8) NOT NULL,
...
)
ALTER TABLE [PAYMENT_RECORD_T] WITH CHECK ADD CONSTRAINT [FK_PAYM4] FOREIGN KEY([PAYMENT_STATUS_CD])
REFERENCES [RECORD_STATUS_T] ([REC_STAT_CD])
GO
CREATE TABLE [RECORD_STATUS_T] (
[RECORD_STAT_CD] [CHAR](8) NOT NULL,
[RECORD_STAT_DSC] [VARCHAR](60) NOT NULL
CONSTRAINT [PK_RECORD_STATUS_T] PRIMARY KEY CLUSTERED (
[RECORD_STAT_CD] ASC
)WITH (PAD_INDEX = OFF...) ON [PRIMARY]
) ON [PRIMARY]
GO
Java:
#Table(name = "PAYMENT_RECORD_T")
#Entity
public class PaymentRecordT {
...
#ManyToOne
#PrimaryKeyJoinColumn(name = "payment_status_cd", referencedColumnName = "REC_STAT_CD")
private RecordStatusT recordStatusT;
}
#Table(name = "RECORD_STATUS_T")
#Entity
public class RecordStatusT {
#Column(name = "REC_STAT_CD")
#Id
private String recStatCd;
#Column(name = "REC_STAT_DSC")
#Basic
private String recStatDsc;
}
Others relations in our app might not be primary key relations but loose relations in which case its just #JoinColumn but the pattern would be the same.
My 'weirdness' is the following:
So in this example I have a list of 10 'Payment Records' each of them have such a record status, which is actually NON NULL in the database. When I do the initial retrieval via EJB method it grabs the 10 records and I also get the correctly resolved/queried record statuses.
Then I add a new record via EJB method (TRANSACTION_REQUIERD). After the add method returns I can query the new payment record in the database via SSMS. Its committed and it looks 100% correct and it contains a correct record status code.
Now I run the retrieval method again and I get the 11 records as I would expect. Only the 11th (newly inserted) record will have recordStatusT as null.
When I restart the app all goes well again for the retrieval of all 11 records. But for subsequent additions the outcome seems again 'undefined'.
In JDBC logging I an see that during the original retrieval of the records the record_status_t table was queried but the 2nd time around it was not and I have no explanation why.
I played with FETCHTYPE.EAGER and read up on caching etc but I'm not going anywhere.
Any ideas?
Thanks for your time
Carsten
I solved the problem by ensuring that after inserts/updates the objects arent being queried from the cache.
In the end - rather than doing it with query hint - I disabled caching for the entity involved using the #Chacheable annotation, like so
#Table(name = "PAYMENT_RECORD_T")
#Entity
#Cacheable(false)
public class PaymentRecordT {
...
#ManyToOne
#PrimaryKeyJoinColumn(name = "payment_status_cd", referencedColumnName = "REC_STAT_CD")
private RecordStatusT recordStatusT;
}
I still feel like there should be a better solution. Eclipselink tracks the inserts/updates so it should be able track what needs rereading from the DB and what not. I still feel like I don't fully understand the entire picture, but this works for me and its reasonably clean.
I can leave the considerable amount of read-only data/objects chacheable and the few that are changeable as non-cacheable.
Thanks for reading
Carsten

ID of java objects not synchronizing with database

The only link I found that's close to what I am experiencing is this one :
How do you synchronize the id of a java object to its associated db row?
and there's not much of a solution in it.
My problem is that my Java objects aren't updated after being added to my database despite the .commit()
em.getTransaction().begin();
System.out.println(eleve.getID());
em.persist(eleve);
em.getTransaction().commit();
System.out.println(eleve.getID());
which refers to this class
public class Eleve {
private String _nom;
private String _prenom;
private float _ptsMerite;
#Id
private int _IDEleve;
and yields this output :
0
0
I think I've done everything properly when it comes to the persistence since it does create the object in the database (mySQL) with correct ID's which I've set to be autoincrement.
I am using javax.persistence for everything (annotations and such).
Did you try to add the #GeneratedValue annotation at your ID field?
There are four possible strategies you can choose from:
GenerationType.AUTO: The JPA provider will choose an appropriate strategy for the underlying database.
GenerationType.IDENTITY: Relies on a auto-increment column in your database.
GenerationType.SEQUENCE: Relies on a database sequence
GenerationType.TABLE: Uses a generator table in the database.
More info: https://www.baeldung.com/jpa-strategies-when-set-primary-key
If you ever change to a more powerful framework it is likely that this manages your transactions (CMT) so you can't (or don't want) commit everytime you want to access the ID for a new entity. In these cases you can use EntityManager#flush to synchronize Entity Manager with database.

Is it ok to save the parent if I am just adding a new child, java hibernate spring?

using hibernate with Java Spring, I have two database tables, Projects and ProjectNotes. Project can have many Notes.
#Entity
#Table(name="projects")
public class Project {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy="project", cascade=CascadeType.ALL)
private List<ProjectNote> projectNotes;
}
and I want to add a new note to the project. Clearly i can do this.
Project project = projectRepository.findOne(id);
ProjectNote pn = new ProjectNote();
pn.setText("Hello");
pn.setProject(project);
project.getProjectNotes().add(pn);
projectRepository.save(project);
and it works. Is that any different than using the notes repository instead
Project project = projectRepository.findOne(id);
ProjectNote pn = new ProjectNote();
pn.setText("Hello");
pn.setProject(project);
projectNotesRepository.save(pn);
Is either more efficient or more correct than the other?
EDIT: another thought. I'm also using #LastModifiedBy/Date on the project. I guess the first method will alter the project lastModified information, whilst the second method will not. But I have not tried it!
In a bidirectional mapping the application is expected to keep the values in sync, meaning that if you call bar.setProject(foo) that method is expected to call foo.getNotes().add(this), making the operations symmetric.
If you enable query logging on Hibernate, you'll see that the end result is the same. The last modified date will also be updated in both cases of course.
Here are some good additional considerations about the complexities of bidirectional associations (and why you might want to avoid them): What is the difference between Unidirectional and Bidirectional associations?

JPA - Setting entity class property from calculated column?

I'm just getting to grips with JPA in a simple Java web app running on Glassfish 3 (Persistence provider is EclipseLink). So far, I'm really liking it (bugs in netbeans/glassfish interaction aside) but there's a thing that I want to be able to do that I'm not sure how to do.
I've got an entity class (Article) that's mapped to a database table (article). I'm trying to do a query on the database that returns a calculated column, but I can't figure out how to set up a property of the Article class so that the property gets filled by the column value when I call the query.
If I do a regular "select id,title,body from article" query, I get a list of Article objects fine, with the id, title and body properties filled. This works fine.
However, if I do the below:
Query q = em.createNativeQuery("select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc",Article.class);
(this is a fulltext search using tsearch2 on Postgres - it's a db-specific function, so I'm using a NativeQuery)
You can see I'm fetching a calculated column, called headline. How do I add a headline property to my Article class so that it gets populated by this query?
So far, I've tried setting it to be #Transient, but that just ends up with it being null all the time.
There are probably no good ways to do it, only manually:
Object[] r = (Object[]) em.createNativeQuery(
"select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc","ArticleWithHeadline")
.setParameter(...).getSingleResult();
Article a = (Article) r[0];
a.setHeadline((String) r[1]);
-
#Entity
#SqlResultSetMapping(
name = "ArticleWithHeadline",
entities = #EntityResult(entityClass = Article.class),
columns = #ColumnResult(name = "HEADLINE"))
public class Article {
#Transient
private String headline;
...
}
AFAIK, JPA doesn't offer standardized support for calculated attributes. With Hibernate, one would use a Formula but EclipseLink doesn't have a direct equivalent. James Sutherland made some suggestions in Re: Virtual columns (#Formula of Hibernate) though:
There is no direct equivalent (please
log an enhancement), but depending on
what you want to do, there are ways to
accomplish the same thing.
EclipseLink defines a
TransformationMapping which can map a
computed value from multiple field
values, or access the database.
You can override the SQL for any CRUD
operation for a class using its
descriptor's DescriptorQueryManager.
You could define a VIEW on your
database that performs the function
and map your Entity to the view
instead of the table.
You can also perform minor
translations using Converters or
property get/set methods.
Also have a look at the enhancement request that has a solution using a DescriptorEventListener in the comments.
All this is non standard JPA of course.

many-to-many (self referencing problem) vs double one-to-many

I'm facing some of the problem some of you already talked about and sorry to bring this out again.
It's just like I still don't get certain aspect of hibernate. I'm using maven2, hibernate 3.2.5 ga, spring 2.6.5 SEC01, hsqldb 1.8.0.10, netbeans 6.7.1.
I'm bulding a user and contat management and I've managed to get a working "many-to-many" relationship between "contact" and "group pojo" in my environment with either hsql or mysql 5.0.51 but not on a local test server .Because of that i'ld like t try the double one-to-many association so there are few things I would like to ask about that mapping with the introduction of an intermediary model.
Now here are my mappings (I've posted just the necessary to avoid long post )
//UserAccount POJO
#OneToMany(targetEntity=PhoneImpl.class, cascade= {CascadeType.ALL})
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Phone> phones = new HashSet<Phone>();
#OneToMany(targetEntity=ContactImpl.class, cascade={CascadeType.ALL}, mappedBy="userAccount")
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Contact> contacts = new HashSet<Contact>();
#OneToMany(targetEntity=GroupImpl.class, cascade={CascadeType.ALL}, mappedBy="userAccount")
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Group> groups = new HashSet<Group>();
//Contact POJO
#ManyToOne(targetEntity=UserAccountImpl.class)
#JoinColumn(name="USER_ACCOUNT_ID",nullable=false)
private UserAccount userAccount;
#OneToMany(targetEntity=PhoneImpl.class, cascade={CascadeType.ALL})
private Set<Phone> phones = new HashSet<Phone>();
#ManyToMany(targetEntity=GroupImpl.class, mappedBy="contacts")
private Set<Group> groups=new HashSet<Group>();
//Group POJO
#ManyToOne(targetEntity=UserAccountImpl.class)
#JoinColumn(name="USER_ACCOUNT_ID",nullable=false)
private UserAccount userAccount;
#ManyToMany(targetEntity=ContactImpl.class,cascade={CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name="GROUP_CONTACT_MAP", joinColumns={#JoinColumn(name="GROUP_ID")},
inverseJoinColumns={#JoinColumn(name="CONTACT_ID")})
private Set<Contact> contacts = new HashSet<Contact>();
SO this works fine on my machine with hsqldb and my local mysql however it's not the same for the local testing server using the same mysql 5.0.SO here are my questions:
1. on the local testing server there is a self referencing foreign key to Contact as well as for group.I've read about circular referencing but can't really tell if it's the case here.Is there in the mapping anything that could generate this kind of self referencing foreign key.Or i failed to avoid the circular referencing stuffs?
2. I'm using Manning java persistence with hibernate which is a great book from where I started practicing the usage of hibernate.in that book the is a double one-to-many association let's say in my case between contact and group with an intermediary model. I just don't know how to use it in practice because I'll like to switch to that approach.
2.1 So my main worries are how to get all the contacts that are in a group and all the groups in which a contact is? I just can't see it clearly.
2.2 if going by that approach how will the contact and group referencing to each other will be? will the ignore each other?
maybe those are basic stuffs for experience java coders like you so forgive my ignorance, i'll be very pleased if you could shed some light on that.
Thanks for reading!
a few ideas:
0: are you sure you've changed the hibernate dialect between using hsqldb and mysql?
1: i think you could expect an exception if you had an unresolvable / circular reference
2.**: you would create a new class called ContactToGroup, it would have a contact field and a group field, each of which is the one end of a many-to-one. Both Contact and Group classes would have a Set of contactToGroup. You could query it like:
from Contact c
left join fetch c.contactToGroupSet joinTable
left join fetch joinTable.Group
This would give you all the Contacts with their Sets of Group attached via ContactToGroup objects.
creating a Contact, ContactToGroup and Group object together would allow you to write to these tables too.
Good luck!

Categories

Resources