child entity is not getting saved in DB using Audit trail using DecriptorEventAdapter eclipse link - eclipselink

When we save the parent Entity Object which contains child entities as a one to many Mapping only Parent is getting inserted in DB.
Below is the postInsert(DescriptorEvent event)
public void postInsert(DescriptorEvent event) {
AbstractEntity ae = (AbstractEntity) event.getObject();
// inserting all the fields as seperate entry to audit table
InsertObjectQuery query = (InsertObjectQuery) event.getQuery();
AuditSession as = new AuditSession();
as.setOperationType("I");
as.setReason("test reason");
long count=1;
for (int i = 0; i < query.getModifyRow().getFields().size(); i++)
{
AuditTrail at = new AuditTrail();
DatabaseField dbField =(DatabaseField)query.getModifyRow().getFields().elementAt(i);
if (dbField == null)
continue;
String fieldName = dbField.getName();
if(query.getModifyRow().getValues(fieldName) != null){
at.setNewValue(query.getModifyRow().getValues(fieldName));
}else{
at.setNewValue(null);
}
at.setFieldId(fieldName);
at.setOldValue(null);
if (ae.getClass().getAnnotation(Table.class) != null) {
at.setTableId(ae.getClass().getAnnotation(Table.class)
.name());
} else {
at.setTableId(ae.getClass().getSimpleName());
}
at.setOperationType("I");
at.setSeqNo(count);
at.setAppRecordId("1");
as.addAuditTrail(at);
count++;
}
event.getSession().insertObject(as);
}
In the above method AuditSession is the parent entity which has OneToMany mapping to AuditTrail entity

All insert, update and delete queries default to cascade private parts, so unless your 1:M mapping is privately owned, the referenced child objects will not be inserted. You can change this on the query using the cascadeAllParts() method as needed:
http://www.eclipse.org/eclipselink/api/2.0/org/eclipse/persistence/queries/DatabaseQuery.html#cascadeAllParts()

Related

Ravendb select - update multiple row

I am trying to select and update multiple row from ravendb, but it recursively update same rows. Namely first 100 rows. There is no changes.
Here is my code. How can I select some rows, Update some fields of each rows and do it again and again until my job finished.
var currentEmailId = 100;
using (var session = store.OpenSession())
{
var goon = true;
while(goon){
var contacts = session.Query<Contacts>().Where(f => f.LastEmailId < currentEmailId).Take(100);
if(contacts.Any()){
foreach(var contact in contacts){
EmailOperation.Send(contact, currentEmailId);
contact.LastEmailId = currentEmailId;
}
session.SaveChanges();
}
else{
goon = false
}
}
}
It's probably because you're doing a query immediately after saving changes, without letting the indexes update after save changes. Thus, you're getting back the same items. To fix that, you can tell SaveChanges to wait until indexes are updated. Your code would look something like this:
Try this:
var goon = true;
var currentEmailId = 100;
while (goon)
{
using (var session = store.OpenSession())
{
var contacts = session.Query<Contacts>()
.Where(f => f.LastEmailId < currentEmailId)
.Take(100);
if(contacts.Any())
{
foreach(var contact in contacts)
{
EmailOperation.Send(contact, currentEmailId);
contact.LastEmailId = currentEmailId;
}
// Wait for the indexes to update when calling SaveChanges.
DbSession.Advanced.WaitForIndexesAfterSaveChanges(TimeSpan.FromSeconds(30), false);
session.SaveChanges();
}
else
{
goon = false
}
}
}
If you're updating many contacts at once, you may wish to consider using using Streaming query results combined with BulkInsert to update many Contacts en mass.

NHibernate named query and multiple result sets

We have a stored procedure that returns several tables. When calling it using NHibernate, we use the bean transformer but only get the first table transformed and all other results are ignored.
I know that NH is able to process several queries in one db trip using futures but we only have one query and it produces a result that is similar to what we would get with futures, but getting this from a stored procedure.
I believe this scenario is quite common but could not find any clues. Is it possible to use NH to retrieve such results?
Yes,you can use MultiQuery "Hack" like this:
The procudure:
CREATE PROCEDURE [dbo].[proc_Name]
AS BEGIN
SELECT * FROM Question
SELECT * FROM Question
END
The NHibernate Query Code:
public void ProcdureMultiTableQuery()
{
var session = Session;
var procSQLQuery = session.CreateSQLQuery("exec [proc_Name] ?,?");// prcodure returns two table
procSQLQuery.SetParameter(0, userId);
procSQLQuery.SetParameter(1, page);
procSQLQuery.AddEntity(typeof(Question));
var multiResults = session.CreateMultiQuery()
.Add(procSQLQuery)
// More table your procedure returns,more empty SQL query you should add
.Add(session.CreateSQLQuery(" ").AddEntity(typeof(Question))) // the second table returns Question Model
.List();
if (multiResults == null || multiResults.Count == 0)
{
return;
}
if (multiResults.Count != 2)
{
return;
}
var questions1 = ConvertObjectsToArray<Question>((System.Collections.IList)multiResults[0]);
var questions2 = ConvertObjectsToArray<Question>((System.Collections.IList)multiResults[1]);
}
static T[] ConvertObjectsToArray<T>(System.Collections.IList objects)
{
if (objects == null || objects.Count == 0)
{
return null;
}
var array = new T[objects.Count];
for (int i = 0; i < array.Length; i++)
{
array[i] = (T)objects[i];
}
return array;
}

NHibernate Change Auditing and Components

An integral part of our architecture as our system must provide a dashboard for users to explicitly publish data changes from environment to another. We looked at NH Evers, but we needed to many domain specific things to be baked into the architecture. We've been successfully using NHibernate's eventing model to track and log state changes (to another table) in our system, but recently stumbled across a snag with Components. When IPostInsertEventListener and IPostUpdateEventListener is fired it publishes a value arrays that denote the current state of the entity. In the case of updates, it publishes an array denoting previous state as well. We are using these arrays to save off "before" and "after" state into our table. When the property is a Component, the actual value (item in the area) is the component instance itself - i.e. an untyped complex object. Short of cross referencing the metamodel mapper and reflecting on that to pull out the individual values, how can I get at the actual values that make up the component? I'm able to get at the column names that map to the Component's members, but not the before and after values.
I've been digging through the NH source, and I'm not finding how to pull out these values, but obviously NH knows how to do this internally as it's able to publish the sql properly. Here's a modified/verbose version of the code I currently have that highlights the issue:
public static RowChangedReport Load(IChangeTrackable trackable, object entityId, AbstractEntityPersister persister, object[] state, object[] oldState)
{
var report = new RowChangedReport
{
Entity = trackable,
EntityTypeFullName = persister.EntityName,
TableName = new TableName(persister.GetTableName()),
EntityId = entityId,
};
var authContext = AuthenticationContext.Current;
if (authContext != null)
report.SecurityContextUserId = authContext.UserId;
if (persister.PropertyNames != null && state != null)
{
report.ChangedType = oldState == null ? RowChangedTypes.New : RowChangedTypes.Modified;
for (var index = 0; index < persister.PropertyNames.Length; index++)
{
var propertyName = persister.PropertyNames[index];
IType propertyType = persister.PropertyTypes[index];
if (!propertyType.IsCollectionType)
{
AddColumnChangeReport(persister, state, oldState, index, propertyName, report);
}
}
}
report.FinalizeState();
return report;
}
private static void AddColumnChangeReport(AbstractEntityPersister persister, object[] state, object[] oldState, int index, string propertyName, RowChangedReport report)
{
var currentValue = state[index];
// for simple properties, this is always a single element array
// for components, this is an array with an element for each member on the component - i.e. how the component is mapped
string[] columns = persister.GetPropertyColumnNames(propertyName);
var previousValue = oldState == null ? null : oldState[index];
if (!Equals(currentValue, previousValue))
{
if (report.ChangedType == RowChangedTypes.Modified && propertyName == IsActivePropertyName)
report.FlagAsDeleted();
foreach (var column in columns)
{
// if this is a component, both the currentValue and the previousValue are complex objects
// need to have a way to get the actual member value per column!
report.AddChange(new ColumnChangedReport(report, propertyName, column, previousValue, currentValue));
}
}
}
OK, after a few hours reading the NH code, I stumbled across Tuplizers and tracked them back to the property Type. So the solution is pretty simple - for components, you need to detect them as such, cast to the ComponentType Type and then ask ComponentType for the property values. Here some code that's working for me:
public static RowChangedReport Load(IChangeTrackable trackable, object entityId, AbstractEntityPersister persister, object[] state, object[] oldState)
{
var report = new RowChangedReport
{
Entity = trackable,
EntityTypeFullName = persister.EntityName,
TableName = new TableName(persister.GetTableName()),
EntityId = entityId,
};
var authContext = AuthenticationContext.Current;
if (authContext != null)
report.SecurityContextUserId = authContext.UserId;
if (persister.PropertyNames != null && state != null)
{
report.ChangedType = oldState == null ? RowChangedTypes.New : RowChangedTypes.Modified;
for (var index = 0; index < persister.PropertyNames.Length; index++)
{
var propertyName = persister.PropertyNames[index];
IType propertyType = persister.PropertyTypes[index];
if (!propertyType.IsCollectionType)
{
AddColumnChangeReport(persister, state, oldState, index, propertyName, propertyType, report);
}
}
}
report.FinalizeState();
return report;
}
private static void AddColumnChangeReport(AbstractEntityPersister persister, object[] state, object[] oldState, int index, string propertyName, IType propertyType, RowChangedReport report)
{
var currentValue = state[index];
string[] columns = persister.GetPropertyColumnNames(propertyName);
var previousValue = oldState == null ? null : oldState[index];
if (!Equals(currentValue, previousValue))
{
if (report.ChangedType == RowChangedTypes.Modified && propertyName == IsActivePropertyName)
report.FlagAsDeleted();
if (propertyType.IsComponentType)
{
ComponentType component = (ComponentType)propertyType;
object[] componentCurrentValues = null;
if (currentValue != null)
componentCurrentValues = component.GetPropertyValues(currentValue, EntityMode.Poco);
object[] componentPreviousValues = null;
if (currentValue != null)
componentPreviousValues = component.GetPropertyValues(previousValue, EntityMode.Poco);
if ((componentCurrentValues != null && componentCurrentValues.Length != columns.Length) ||
(componentPreviousValues != null && componentPreviousValues.Length != columns.Length))
throw new ConventionViolationException(GetComponentArraysExceptionMessage(persister, propertyName, columns, componentPreviousValues, componentCurrentValues));
for (int i = 0; i < columns.Length; i++)
{
var column = columns[i];
var componentPreviousValue = componentPreviousValues == null ? null : componentPreviousValues[i];
var componentCurrnetValue = componentCurrentValues == null ? null : componentCurrentValues[i];
report.AddChange(new ColumnChangedReport(report, propertyName, column, componentPreviousValue, componentCurrnetValue));
}
}
else
{
if (columns.Length > 1)
throw new ConventionViolationException("Expected only component properties to have multiple columns. Property '{0}' on entity {1} is violating that assumption.".FormatWith(propertyName, persister.EntityName));
report.AddChange(new ColumnChangedReport(report, propertyName, columns[0], previousValue, currentValue));
}
}
}

Existing posts keep on re-add upon deletion of selected row in jTable

I try to refresh the data of jTable upon deletion of selected row. Here are my codes to set up table :
private JTable getJTableManageReplies() {
jTableManageReplies.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
jTableManageReplies.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
int viewRow = jTableManageReplies.getSelectedRow();
// Get the first column data of the selectedrow
int replyID = Integer.parseInt(jTableManageReplies.getValueAt(
viewRow, 0).toString());
eForumRepliesAdmin reply = new eForumRepliesAdmin(replyID);
replyID = JOptionPane.showConfirmDialog(null, "Are you sure that you want to delete the selected reply? " , "Delete replies", JOptionPane.YES_NO_OPTION);
if(replyID == JOptionPane.YES_OPTION){
reply.deleteReply();
JOptionPane.showMessageDialog(null, "Reply has been deleted successfully.");
SetUpJTableManageReplies();
}
}
}
});
return jTableManageReplies;
}
public void SetUpJTableManageReplies() {
DefaultTableModel tableModel = (DefaultTableModel) jTableManageReplies
.getModel();
String[] data = new String[5];
db.setUp("IT Innovation Project");
String sql = "Select forumReplies.reply_ID,forumReplies.reply_topic,forumTopics.topic_title,forumReplies.reply_content,forumReplies.reply_by from forumReplies,forumTopics WHERE forumReplies.reply_topic = forumTopics.topic_id ";
ResultSet resultSet = null;
resultSet = db.readRequest(sql);
jTableManageReplies.repaint();
tableModel.getDataVector().removeAllElements();
try {
while (resultSet.next()) {
data[0] = resultSet.getString("reply_ID");
data[1] = resultSet.getString("reply_topic");
data[2] = resultSet.getString("topic_title");
data[3] = resultSet.getString("reply_content");
data[4] = resultSet.getString("reply_by");
tableModel.addRow(data);
}
resultSet.close();
} catch (Exception e) {
System.out.println(e);
}
}
And this is my sql statement :
public boolean deleteReply() {
boolean success = false;
DBController db = new DBController();
db.setUp("IT Innovation Project");
String sql = "DELETE FROM forumReplies where reply_ID = " + replyID
+ "";
if (db.updateRequest(sql) == 1)
success = true;
db.terminate();
return success;
}
I called the repaint() to update the table data with the newest data in database and it works. I mean the data after deletion of certain row. However, the existing posts will keep on re-add. Then I add the removeAllElement method to remove all the existing posts because my sql statement is select * from table. Then, there is an error message which is ArrayIndexOutOfBoundsException. Any guides to fix this? Thanks in advance.
I called the repaint() to update the table data with the newest data
in database and it works.
There is no need to call repaint method when data is changed. Data change is handled by the Table Model (DefaultTableModel in this case.) And fireXXXMethods are required to be called whenever data is changed but you are using DefaultTableModel even those are not required. (Since by default it call these methods when ever there is a change.)
I think the problem is in the valuesChanged(..) method. You are getting the value at row 0 but not checking whether table has rows or not. So keep a constraint.
int viewRow = jTableManageReplies.getSelectedRow();
// Get the first column data of the selectedrow
if(jTableManageReplies.getRowCount() > 0)
int replyID = Integer.parseInt(jTableManageReplies.getValueAt(viewRow, 0).toString());

Get children from flat list

I've got a flat list of items in a ObservableCollection. These items have the properties item.Id and item.Parent.Id.
I've been given the id of the parent (top level), now with this id I need to iterate through the list and find the children of this parent. Each child can only have one parent, a parent can have multiple childs.
How can I do this effectively?
You can use:
var childrenOfParent = theCollection.Where(item => item.Parent.Id == parentId);
Edit in response to comments:
Given that you have a hierarchical dataset, I would personally make a routine that checks to see if a given item has a specific item as a parent recursively, like so:
bool HasParent(Item item, int parentId)
{
if (item.Parent == null)
return false;
else if (item.Parent.Id == parentId)
return true;
else
return HasParent(item.Parent, parentId);
}
Given this, you could use:
var childrenOfParent = theCollection.Where(item => HasParnet(item, parentId));
Well I got it, however, if anyone can optimize/refactor this code into something more efficient please let me know and I'll mark your reaction as an answer. As long as I learn :).
foreach (var item in myitemlist) // first put the top level parent in the queue
{
if (parentitem.Id == item.Id)
{
filteredChildrenQueue.Enqueue(item);
}
}
while (!stillsearching)
{
if (filteredChildrenQueue.Count == 0)
{
stillsearching = true;
return;
}
FindParentChild();
}
Keep calling this method and work the first item in the queue
private void FindParentChild()
{
foreach (var item in myitemlist)
{
if (item.Parent.Id == filteredChildrenQueue.ElementAt(0).Id)
{
filteredChildrenQueue.Enqueue(item);
}
}
filteredChildrenList.Add(filteredChildrenQueue.ElementAt(0));
filteredChildrenQueue.Dequeue();
}
filteredChildrenList will contain the top level parent + all childs it contains.