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.
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;
}
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));
}
}
}
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());
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.