JdbcPollingChannelAdapter update using only the id -- no bean - spring

I have the following bean definition:
#Bean
JdbcPollingChannelAdapter jdbcPollingChannelAdapter() {
// Get all the pending jobs
JdbcPollingChannelAdapter adapter = new JdbcPollingChannelAdapter(jdbcTemplate, "select id from poller_jobs where status = 'PENDING'");
// Immediately mark them as running so the next jdbc poll doesn't re-process them
adapter.setUpdateSql("update poller_jobs set status='RUNNING' where id in (:id)");
adapter.setMaxRowsPerPoll(100);
adapter.setRowMapper((r, i) -> r.getLong("id"));
return adapter;
}
And this fails because the row mapper maps to just a long id and so the adapter doesn't know how to grab the ID that I need in the update. Anyone know how to do that without needing to select * and map to a full object? That seems like more overhead than I really need.

This works for me:
<inbound-channel-adapter data-source="dataSource"
channel="target"
query="select id from item where status=2"
update="update item set status=10 where id in (:#root)"
update-per-row="true"
row-mapper="columnRowMapper"/>
<beans:bean id="columnRowMapper" class="org.springframework.jdbc.core.SingleColumnRowMapper"/>
So, the first is (:#root) as param placeholder just because the default setUpdateSqlParameterSourceFactory() is ExpressionEvaluatingSqlParameterSourceFactory, where the root object of evaluation context is the result of SELECT or, as it is stand by the update-per-row="true", each row in the ResultSet:
if (payload != null && this.updateSql != null) {
if (this.updatePerRow) {
for (Object row : payload) {
executeUpdateQuery(row);
}
}
else {
executeUpdateQuery(payload);
}
}
Therefore what you need in your configuration is this two lines of code:
adapter.setUpdateSql("update poller_jobs set status='RUNNING' where id in (:#root)");
adapter.setUpdatePerRow(true);
The SingleColumnRowMapper does the trick for really single column in the ResultSet, BTW.

Related

Spring Boot JPA : identifier of an instance of bingo.model.Group was altered from 1702 to null

I have a Short Question:
Last Save is working(Last Save will be Update).
But First Save is not working.(First Save will be Insert)
I can't insert this way, how is it possible?
#GetMapping(value = "/delete/{id}")
public String delete(#PathVariable BigInteger id, Model model) {
try {
Group group = groupService.findById(id);
group.setId(null);
group.isLog(true);
// This Save will be Insert Data
groupService.save(group);
group = groupService.findById(id);
group.isLog(true);
//This Save will be Update Data
groupService.save(group);
return "redirect:/accountsGroup/";
} catch (Exception ex) {
return "masters/accountsInfo/groups/index";
}
}
You can't just set the ID null.
The entity is in a managed state and will not be new just because you set the ID to null.
The proper way would be to clone the entity state in a new instance.
You could also try to detach the entity (EntityManager.detach) and then set the ID to null. Maybe it will insert a new row. But as I said this is not the way you should do that.
Read more about entity states here: https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/

#UniqueConstraints failing on update

I have an Entity class in which I put uniqueconstraint annotation
#Table(uniqueConstraints={#UniqueConstraint(columnNames={"staffRecord_id", "defaultLabel_id","company_id","keyCode"})})
public class AllowanceDeduction implements Serializable{
---
What I have noticed that is when I try to save on the table
using
if (allowanceDeduction.getId() == null) {
this.entityManager.persist(allowanceDeduction);
} else {
this.entityManager.merge(allowanceDeduction);
}
when the save or update fails due to a unique constraint. Isn't it only supposed to fail when trying to save a new record that is identical to a record that already exist.
Why would it fail when trying to merge or update?
Please help needed
I can't say for sure but it looks like you're trying to persist a null id
if (allowanceDeduction.getId() == null) {
this.entityManager.persist(allowanceDeduction);
/* don't you need the id set to a non-null value in order to persist it? */
} else {

How to create a update function in LINQ when object has a list?

I´m still having a hard time with Linq.
I need to write a Update Function tat receives an object that has a list. Actually, A region has a list of cities. I want to pass an object "Region" that has a name filed and a list of cities. The problem, is the city objects came from another context and I am unable to attach them to this context. I have been trying several functions, and always get an error like "EntitySet was modified during enumeration" or other. I am tring to make the code below work, but if anyone has a different approach please help.
public int Updateregion(region E)
{
try
{
using (var ctx = new AppDataDataContext())
{
var R =
(from edt in ctx.regiaos
where edt.ID == E.ID
select edt).SingleOrDefault();
if (R != null)
{
R.name = R.name;
R.description = E.description;
}
R.cities = null;
R.cities.AddRange(Edited.Cities);
ctx.SubmitChanges();
return 0 //OK!
}
}
catch (Exception e)
{
......
}
You can't attach objects retrieved from one datacontext to another, it's not supported by Linq-to-SQL. You need to somehow dettach the objects from their original context, but this isn't supported either. One can wonder why a dettach method isn't available, but at least you can fake it by mapping the list to new objects:
var cities = Edited.Cities.Select(city => new City {
ID = city.ID,
Name = city.Name,
/* etc */
});
The key here is to remember to map the primary key and NOT map any of the relation properties. They must be set to null. After this, you should be able to attach the new cities list, and have it work as expected.

NHibernate 3.0 IQueryable selecting ghost column

I'm getting a strange error when running what appears to be a simple query.
return (from x in session.Query<Contact>()
.Where(x => x.Id == 10)
select new ContactIndexViewModel
{
Id = x.Id,
Name = x.BasicInfo.FirstName + " " + x.BasicInfo.LastName,
Filters = x.Filters
}).FirstOrDefault();
Is generating the following SQL
select
contact0_.[Id] as col_0_0_,
contact0_.[BasicInfoFirstName] as col_1_0_,
contact0_.[BasicInfoLastName] as col_2_0_,
. as col_3_0_,
filters1_.[Id] as column1_16_,
filters1_.Criteria1 as Criteria2_16_,
// .. .more filters1_ fields
filters1_.ContactId as ContactId16_
from
[MyServer].[dbo].[Contact] contact0_
inner join [MyServer].[dbo].[Filter] filters1_
on contact0_.[Id]=filters1_.ContactId
where
contact0_.[Id]=#p0
Notice the fourth column being selected. BasicInfo is a component and the select (in the query) includes all the fields defined in the ViewModel.
I am not having any other problems with the Contact or Filter objects in other parts of the application. Contact -> Filter has a one to many relationship.
Any idea's on how to debug or what may cause this?
UPDATE
If I remove the reference to Filters in the select, the problem goes away.
UPDATE Relevant Mappings
Contact
public partial class ContactMap : ClassMap<Contact>
{
/// <summary>Initializes a new instance of the <see cref="ContactMap"/> class.</summary>
public ContactMap()
{
Table("[MyServer].[dbo].[Contact]");
OptimisticLock.Version();
DynamicUpdate();
LazyLoad();
Id(x=>x.Id)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[Id]")
.GeneratedBy.Identity();
Version(x=>x.RecordVersion)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[RecordVersion]")
.CustomSqlType("timestamp")
.Not.Nullable()
.UnsavedValue("null")
.CustomType("BinaryBlob")
.Generated.Always();
Map(x=>x.Active).Access.CamelCaseField(Prefix.Underscore);
// other scalar properties
Component(x0=>x0.BasicInfo, m0=>
{
m0.Map(x1=>x1.FirstName).Column("[BasicInfoFirstName]").Access.CamelCaseField(Prefix.Underscore);
m0.Map(x1=>x1.LastName).Column("[BasicInfoLastName]").Access.CamelCaseField(Prefix.Underscore);
// other scalar properties
});
// other relationships
HasMany(x=>x.Searches)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Fetch.Select()
.Inverse()
.LazyLoad()
.KeyColumns.Add("ContactId");
}
}
Search
public partial class SearchMap : ClassMap<Search>
{
public SearchMap()
{
Table("[MyServer].[dbo].[Search]");
OptimisticLock.Version();
DynamicUpdate();
LazyLoad();
Id(x=>x.Id)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[Id]")
.GeneratedBy.Identity();
Map(x=>x.Controller).Not.Nullable().Access.CamelCaseField(Prefix.Underscore);
Map(x=>x.Module).Not.Nullable().Access.CamelCaseField(Prefix.Underscore);
Map(x=>x.Name).Column("[Name]").Not.Nullable().Access.CamelCaseField(Prefix.Underscore);
References(x=>x.Contact)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.All()
.Fetch.Select()
.Columns("ContactId");
HasMany(x=>x.DataFilters)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Fetch.Select()
.Inverse()
.LazyLoad()
.KeyColumns.Add("SearchId");
}
}
Did you map Filters with FetchMode.Join?
By the way, it may be easier to create the ContactIndexViewModel in memory, with the trade off that it fetches too many columns from the database. On the other side, Get doesn't flush the session, which may be performance relevant.
var contact = session.Get<Contact>(10);
return new ContactIndexViewModel
{
Id = contact.Id,
Name = contact.BasicInfo.FirstName + " " + contact.BasicInfo.LastName,
Filters = contact.Filters
};
Your mapping for the table is unusual to me.
Table("[MyServer].[dbo].[Contact]");
Normally the server name is provided during configuration, the schema is stated separately, and the delimiters ("[...]") are set by NHibernate. I would map it as:
Schema("dbo");
Table("Contact");
That may be causing a parsing problem leading to the odd select. If that's not it, then I think it's a bug -- NHibernate should never issue a select without a table alias and column name.

Auditing in Entity Framework

After going through Entity Framework I have a couple of questions on implementing auditing in Entity Framework.
I want to store each column values that is created or updated to a different audit table.
Right now I am calling SaveChanges(false) to save the records in the DB(still the changes in context is not reset). Then get the added | modified records and loop through the GetObjectStateEntries. But don't know how to get the values of the columns where their values are filled by stored proc. ie, createdate, modifieddate etc.
Below is the sample code I am working on it.
// Get the changed entires( ie, records)
IEnumerable<ObjectStateEntry> changes = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
// Iterate each ObjectStateEntry( for each record in the update/modified collection)
foreach (ObjectStateEntry entry in changes)
{
// Iterate the columns in each record and get thier old and new value respectively
foreach (var columnName in entry.GetModifiedProperties())
{
string oldValue = entry.OriginalValues[columnName].ToString();
string newValue = entry.CurrentValues[columnName].ToString();
// Do Some Auditing by sending entityname, columnname, oldvalue, newvalue
}
}
changes = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added);
foreach (ObjectStateEntry entry in changes)
{
if (entry.IsRelationship) continue;
var columnNames = (from p in entry.EntitySet.ElementType.Members
select p.Name).ToList();
foreach (var columnName in columnNames)
{
string newValue = entry.CurrentValues[columnName].ToString();
// Do Some Auditing by sending entityname, columnname, value
}
}
Here you have two basic options:
Do it at the database level
Do it in the c# code
Doing it at the data base level, means using triggers. In that case there is no difference if you are using enterprise library or another data access technology.
To do it in the C# code you would add a log table to your datamodel, and write the changes to the log table. When you do a save changes both the changes to the data and the information which you wrote to the log table would be saved.
Are you inserting the new record using a stored proc? If not (i.e. you are newing up an object, setting values, inserting on submit and then saving changes the new object id will be automatically loaded into the id property of the object you created. If you are using a stored proc to do the insert then you need to return the ##IDENTITY from the proc as a return value.
EX:
StoreDateContext db = new StoreDataContext(connString);
Product p = new Product();
p.Name = "Hello Kitty Back Scratcher";
p.CategoryId = 5;
db.Products.Add(p);
try
{
db.SaveChanges();
//p.Id is now set
return p.Id;
}
finally
{
db.Dispose;
}

Resources