get primary key of last inserted record with JPA - postgresql

I've been using JPA to insert entities into a database but I've run up against a problem where I need to do an insert and get the primary key of the record last inserted.
Using PostgreSQL I would use an INSERT RETURNING statement which would return the record id, but with an entity manager doing all this, the only way I know is to use SELECT CURRVAL.
So the problem becomes, I have several data sources sending data into a message driven bean (usually 10-100 messages at once from each source) via OpenMQ and inside this MDB I persists this to PostgreSQL via the entity manager. It's at this point I think there will be a "race condition like" effect of having so many inserts that I won't necessarily get the last record id using SELECT CURRVAL.
My MDB persists 3 entity beans via an entity manager like below.
Any help on how to better do this much appreciated.
public void onMessage(Message msg) {
Integer agPK = 0;
Integer scanPK = 0;
Integer lookPK = 0;
Iterator iter = null;
List<Ag> agKeys = null;
List<Scan> scanKeys = null;
try {
iag = (IAgBean) (new InitialContext()).lookup(
"java:comp/env/ejb/AgBean");
TextMessage tmsg = (TextMessage) msg;
// insert this into table only if doesn't exists
Ag ag = new Ag(msg.getStringProperty("name"));
agKeys = (List) (iag.getPKs(ag));
iter = agKeys.iterator();
if (iter.hasNext()) {
agPK = ((Ag) iter.next()).getId();
}
else {
// no PK found so not in dbase, insert new
iag.addAg(ag);
agKeys = (List) (iag.getPKs(ag));
iter = agKeys.iterator();
if (iter.hasNext()) {
agPK = ((Ag) iter.next()).getId();
}
}
// insert this into table always
iscan = (IScanBean) (new InitialContext()).lookup(
"java:comp/env/ejb/ScanBean");
Scan scan = new Scan();
scan.setName(msg.getStringProperty("name"));
scan.setCode(msg.getIntProperty("code"));
iscan.addScan(scan);
scanKeys = (List) iscan.getPKs(scan);
iter = scanKeys.iterator();
if (iter.hasNext()) {
scanPK = ((Scan) iter.next()).getId();
}
// insert into this table the two primary keys above
ilook = (ILookBean) (new InitialContext()).lookup(
"java:comp/env/ejb/LookBean");
Look look = new Look();
if (agPK.intValue() != 0 && scanPK.intValue() != 0) {
look.setAgId(agPK);
look.setScanId(scanPK);
ilook.addLook(look);
}
// ...

The JPA spec requires that after persist, the entity be populated with a valid ID if an ID generation strategy is being used. You don't have to do anything.

Related

Force Entity Framework to read each line with all details

I am having trouble with en EF method returning duplicate rows of data. When I am running this, in my example, it returns four rows from a database view. The fourth row includes details from the third row.
The same query in SSMS returns four individual rows with the correct details. I have read somewhere about EK and problems with optimization when there are no identity column. But - is there anyway to alter the below code to force EK to read all records with all details?
public List<vs_transactions> GetTransactionList(int cID)
{
using (StagingDataEntities db = new StagingDataEntities())
{
var res = from trans in db.vs_transactions
where trans.CreditID == cID
orderby trans.ActionDate descending
select trans;
return res.ToList();
}
}
Found the solution :) MergeOption.NoTracking
public List<vs_transactions> GetTransactionList(int cID)
{
db.vs_transactions.MergeOption = MergeOption.NoTracking;
using (StagingDataEntities db = new StagingDataEntities())
{
var res = from trans in db.vs_transactions
where trans.CreditID == cID
orderby trans.ActionDate descending
select trans;
return res.ToList();
}
}

Saving changes if adding to context

EF Core 2.2.x
The question Im trying to answer is if I can limit my call of SaveChanges to only once or do I need it each time I add an entity, since in the next lower level I need to query the DbContext to see if the item exists.
Am trying to "merge" some data between two sets of similar tables. So in the process of adding records and drilling down the hierarchy, I have to first see if a record exists and if not, then add it.
/*
List<StagedOrder> sorders;
StagedOrder
Id
Number
InsertDate
Notes
StagedOrderLine
Id
ItemNo
Qty
StagedOrderKey
*/
foreach( StagedOrder s in sorders) {
// get order
Order o = (from a in ctx.Orders where a.number = s.Number select a).FirstOrDefault();
if(o == null) {
o = new Order() {
Number = s.Number,
InsertDate = DateTime.Now,
Notes = "imported order"
};
ctx.Orders.Add(o);
// ctx.SaveChanges() ???
}
// get lines for orders
OrderLines ol = (from a in ctx.DetailLines where a.OrderKey = o.Id select a).ToList();
if(ol == null) {
foreach(StagedOrderLine l in s.StagedOrderLines) {
ol = new DetailLine() {ItemNo = l.ItemNo, Qty = l.Qty, OrderKey = l.Id}
ctx.DetailLines.Add(ol);
ctx.SaveChanges();
}
}
}
// can I limit my call to savechanges only once here?
// ctx.SaveChanges();
Also, should I create a new DetailLine using an Order.Id or an entity reference, eg.
ol = new DetailLine() {ItemNo = l.ItemNo, Qty = l.Qty, Order = o}

How to log new records during SaveChanges

I want to log new and modified records. This code works just fine for Modified Records.
But with Added records, there is an issue. Since it is new to the Database, there is not yet a primary key for it. So there is no way to log which record was added.
However, if I try to log the records after the save, the EntityState is no longer Added. So I don't know what was added.
The only solution I have been able to come up with is to save a list of the new records, and then after the save, then Log the changes. But that seems like a workaround.
Is there some way to resolve this?
private List<Event> LogChanges(EntityEntry entityEntry, Enums.TableNames tableName)
{
List<Event> result = new List<Event>();
var databaseValues = entityEntry.GetDatabaseValues();
foreach (var property in entityEntry.CurrentValues.Properties.Where(a=> a.Name !="TenantId"))
{
string original = databaseValues[property]?.ToString();
string current = entityEntry.CurrentValues[property]?.ToString();
if(!object.Equals(original,current))
{
result.Add(
new Event()
{
AppUserId = this._appUserProvider.CurrentAppUserId,
EventDate = DateTimeOffset.UtcNow,
EventTypeId = (int)Enums.EventTypes.Modified,
TenantId = databaseValues.GetValue<int>("TenantId"),
RecordId = databaseValues.GetValue<int>("Id"),
ColumnName = property.Name,
OriginalValue = original,
NewValue = current,
TableId = (int)tableName
});
}
}
return result;
}
This library adds triggers to EntityFrameworkCore. Using the Triggers it provides is a much cleaner way to accomplish the above.

EF6 update not actually updating the table record?

I'm having to write a app that effectively copies data from one databaseA.table to databaseB.table but there are a few fields in databaseB that aren't in databaseA.
I've come up with basic code below. The insert works and the update doesn't trow an error, however, the update doesn't actually update any records.
I've confirmed that the bcEmployee object in the update has the new values from databaseA like it should. The employee object is the record from databaseA.
Am I missing something to make this update?
BC_employee bcEmployee = new BC_employee();
bcEmployee.emp_id = employee.emp_id;
bcEmployee.emp_firstname = employee.emp_firstname;
bcEmployee.emp_lastname = employee.emp_lastname;
using (BCcontext ctx = new BCcontext())
{
var existBCemployee = ctx.employee.Find(employee.emp_id);
if (existBCemployee == null) //Insert
{
//Set default values that aren't in the original database
bcEmployee.emp_paystat = null;
bcEmployee.password = null;
bcEmployee.enroll_date = null;
ctx.employee.Add(bcEmployee);
}
else
{
ctx.Entry(existBCemployee).CurrentValues.SetValues(bcEmployee);
}
ctx.SaveChanges();
}

Linq to enties, insert foreign keys

I am using the ADO entity framework for the first time and am not sure of the best way of inserting db recored that contain foreign keys.
this is the code that i am using, I would appreciate any comments and suggestion on this.
using (KnowledgeShareEntities entities = new KnowledgeShareEntities())
{
Questions question = new Questions();
question.que_title = questionTitle;
question.que_question_text = questionText;
question.que_number_of_views = 0;
question.que_is_anonymous = isAnonymous;
question.que_last_activity_datetime = DateTime.Now;
question.que_timestamp = DateTime.Now;
question.CategoriesReference.Value = Categories.CreateCategories(categoryId);
question.UsersReference.Value = Users.CreateUsers(userId);
entities.AddToQuestions(question);
entities.SaveChanges();
return question.que_id;
}
You should use something like
question.UsersReference.EntityKey = new EntityKey("MyEntities.Users",
"ID", userId);
You don't have to have User object to set up foreign key, just use ID.