How do I get inserted key in ItemWriter to use it in another query Spring Batch - spring-batch

I am using Spring Batch, and I will do an Insert in ItemWriter and need to do an update in another table with the key inserted. How do I get the key to use it in the updateTableB ?
Thank you in advance.
#Bean
public CompositeItemWriter<TableA> distributionCompositeItemWriter() {
CompositeItemWriter<TableA> compositeItemWriter = new CompositeItemWriter<>();
compositeItemWriter.setDelegates(Arrays.asList(processTableA(), updateTableB()));
return compositeItemWriter;
}
#Bean
public ItemWriter<TableA> processTableA() {
return new JdbcBatchItemWriterBuilder<TableA>()
.dataSource(dataSource)
.sql("insert into tableA (id, name) values (tableA_id_seq.nextval, :name)")
.beanMapped()
.build();
}
#Bean
public ItemWriter<TableA> updateTableB() {
return new JdbcBatchItemWriterBuilder<TableA>()
.dataSource(dataSource)
.sql("update tableB set tableB_key = :tableA_key where id = :another_field)")
.beanMapped()
.build();
}

You could add the id attribute to the TableA class and use an ItemProcessor to populate it (eg. select tableA_id_seq.nextval from dual), then modify the processTableA ItemWriter to use the pre-populated id value (eg. insert into tableA (id, name) values (:id, :name). Then you also have the id available in the updateTableB ItemWriter.

Related

JPA - perform an insert on a Postgres table whose primary key is generated from a database trigger

I am writing an API where I am inserting a record into a table (Postgres). I was hoping to use JPA for the work. Here is the potential challenge: the primary key for the insert is generated from a database trigger, rather than from sequence count or similar. In fact, the trigger creates the primary key using the values of other fields being passed in as part of the insert. So for example,
if I have a entity class like the following:
#Entity
#Validated
#Table(name = "my_table", schema="common")
public class MyModel {
#Id
#Column(name = "col_id")
private String id;
#Column(name = "second_col")
private String secCol;
#Column(name = "third_col")
private String thirdCol;
public MyModel() {
}
public MyModel(String id, String secCol, String thirdCol) {
this.id = id;
this.secCol = secCol;
this.thirdCol = thirdCol;
}
}
I would need the col_id field to somehow honor that the key is generated from the trigger, and the trigger would need to be able to read the values for second_col and third_col in order to generate the primary key. Finally, I would need the call to return the value of the primary key.
Can this be done with jpa and repository interface such as:
public interface MyRepo extends JpaRepository <MyModel, String> {
}
and then use either default save method such as myRepo.saveAndFlush(myModel) or custom save methods? I can't find anything on using JPA with DB triggers that generating keys. If it cannot be done with JPA, I would be grateful for any alternative ideas. Thanks.
ok, I was able to get this to work. It required writing a custom query that ignored the primary key field:
public interface MyRepo extends JpaRepository <MyModel, String> {
#Transactional
#Modifying
#Query(value = "INSERT INTO my_table(second_col, third_col)", nativeQuery = true)
int insertMyTable(#Param("second_col") String second_col, #Param("third_col") String third_col);
}
The model class is unchanged from above. Because it was executed as a native query, it allowed postGres to do its thing uninterrupted.

How Entity Framework Updates Entity after DB update call

I have class Customer and Order class.
Customer class has Order Class And DeliveryAddress class as virtual property
class Customer
{
public int custId{get;set;}
public string custName{get;set;}
[ForiegnKey("custId")]
public virtual ICollection<Order> Orders{get;set;}
[ForiegnKey("custId")]
public virtual ICollection<DeliveryAddress> Addresses{get;set;}
}
class Order
{
public int OrderId{get;set;}
public int custId{get;set;}
public string OrdDescription{get;set;}
[ForiegnKey("custId")]
public virtual Customer Customer{get;set;}
}
public class DomainService
{
public void CreateAndSave()
{
Customer A = new Customer();
mycontext.Entity(A).State= EntityState.Added;
Order ord1= new Order();
mycontext.Entity(ord1).State= EntityState.Added;
Order ord2= new Order();
mycontext.Entity(ord2).State= EntityState.Added;
DeliveryAddress adr1= new DeliveryAddress();
mycontext.Entity(adr1).State= EntityState.Added;
DeliveryAddress adr2= new DeliveryAddress();
mycontext.Entity(adr2).State= EntityState.Added;
A.DeliveryAddress.Add(adr1);
A.DeliveryAddress.Add(adr2);
mycontext.SaveChanges();
//After added records into Customer and Order table.I immediately call stored procedure which will update few columns in the newly added records
}
}
My Question is how do i update the in-memory instance of Customer and Order class instances in mycontext to get reflected with the changes done at stored procedure.
I have tried using Reload method but A.Orders shows count of 4, 2 records from above code and another 2 as Dynamic Proxies objects.
Another problem with dynamic proxies are when i update columns in one of the record in DeliveryAddress table, it is trying to update all column in Order table.
Please help how to reload and track object in in-memory so that i can proceed with further update using context in EF

Entity framework table names mapping at runtime

I need to import a huge amount of data without db work interruption. So there are two tables: Data and DataTemp(they are identical). At first data is uploaded to the temp table and then tables are swapped by backing up Data table and renaming DataTemp to Data(this example is simplified - there much more than two tables). Entity Framework is used in this project.
So the question is: is it possible to use entity framework to use DataTemp without duplicating the Data table in the schema? Is there a way to edit final sql before executing it to temporary replace table names? OnModelCreating doesn't fit because it's called once but I need to use both tables at different times.
Thanks!
public class Datum
{
public int Id { get; set; }
}
public class DataContext : DbContext
{
public DbSet<Datum> Data { get; set; }
}
public class DataTempContext : DataContext
{
protected override void OnModelCreating(DbModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Datum>().ToTable("DataTemp");
}
}
EDIT
This is working for me.
Use of old:
using (var context = new DataContext())
{
// illustrate original table name, Data
Console.WriteLine(context.Data.ToString());
Console.WriteLine();
// Add some real data, for LinqPad
context.Data.Add(new Datum());
context.SaveChanges();
}
SQL output by WriteLine:
SELECT
[Extent1].[Id] AS [Id],
FROM [Data] AS [Extent1]
Use of new:
using (var context1 = new DataContext())
using (var context2 = new DataTempContext())
{
// copy old table to new
foreach (var d in context1.Data)
context2.Data.Add(d);
context2.SaveChanges();
}
using (var context = new DataTempContext())
{
// illustrate different table name, DataTemp
Console.WriteLine(context.Data.ToString());
Console.ReadKey();
}
SQL output by WriteLine:
SELECT
[Extent1].[Id] AS [Id],
FROM [DataTemp] AS [Extent1]

Entity Framework: Return GUID from code-first stored procedure insert?

I have an InsertPerson stored procedure and i need it to return a GUID (Uniqueidentifier) into my person object that is being created. Is this even possible with entity framework codefirst? I've tried everything and entity framework is ignoring the guid im trying to return using an output parameter. Does anyone have an example if this is possible?
Person.cs:
public class Person
{
public Guid Id { get; set; }
public string FirstName { get; set; }
}
Stored Procedure:
CREATE PROCEDURE [dbo].[InsertPerson]
#KeyPlayerId UNIQUEIDENTIFIER ,
#FirstNameNVARCHAR(255)
AS
-- Perform Insert
insert into [dbo.].[Person]....
-- Return GUID
select #Id as [Id];
END;
I was able to accomplish it by telling Entity Framework that the Id is a Database Generated value. I mapped the Insert procedure to InsertPerson in the PersonMap class and use that when the model is created in the OnModelCreating method. In the stored procedure I generate a new Id and pass this back to Entity Framework. Hope this helps!
PersonMap.cs
public class PersonMap : EntityTypeConfiguration<Person>
{
public PersonMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Tell Entity Framework the database will generate the key.
this.Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Properties
this.Property(t => t.FirstName)
.IsRequired()
.HasMaxLength(255);
//Map to Stored Procedure
this.MapToStoredProcedures(s => s.Insert(i => i.HasName("InsertPerson")));
}
}
OnModelCreating
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new PersonMap());
}
InsertPerson Stored Procedure
CREATE PROCEDURE InsertPerson
-- Since Id is marked as Database Generated we only need
-- a parameter for First Name
#FirstName nvarchar(255) = 0
AS
-- Variable to hold new Id
DECLARE #Id uniqueidentifier
-- Generate a new Id using NEWID function that returns a unique identifier
SET #Id = NEWID()
-- Perform Insert
INSERT INTO [dbo].[Person] VALUES (#Id, #FirstName)
-- Return the Id to Entity Framework
SELECT #Id AS 'Id'
I think this may help you:
var sqlConnection1 = new SqlConnection("Your Connection String");
var cmd = new SqlCommand();
var reader;
cmd.CommandText = "StoredProcedureName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = sqlConnection1;
sqlConnection1.Open();
reader = cmd.ExecuteReader();
rdr = cmd.ExecuteReader();
var outputValue = Guid.Parse(cmd.Parameters["#Response"].Value.ToString());
sqlConnection1.Close();

JPA not insert the data in the row after persist

I am using an ID generated value in my entity
#Id
#TableGenerator(
name="marcaTable",
table="JPA_WXS_APP_SEQUENCE_GENERATOR",
pkColumnName="GEN_KEY",
valueColumnName="GEN_VALUE",
pkColumnValue="MARCA_ID",
allocationSize=1,
initialValue=0)
#GeneratedValue(strategy=GenerationType.TABLE,generator="marcaTable")
public int getId() {
return Id;
}
I use a table to save the id.
If I execute this code twice its fail because there are duplicates ID (1 id)
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("ACoches");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
marca nmarca2 = new marca();
nmarca2.setNombre_marca("pepito");
em.flush();
em.persist(nmarca2);
tx.commit();
em.close();
emf.close();
}
}
But if I manually execute a select of marca table it is empty, it seems that JPA dont insert the data in the row just when i make the em.persist(nmarca2);
If I delete the JPA_WXS_APP_SEQUENCE_GENERATOR table manually and I select again the marca table now yes I can see the register.
Thanks in advance!!!
persist() just registers the object to be persisted. It will not be inserted until commit() or flush(). If you call flush() after the persist() it will have been inserted.
Can't see why you would get a duplicate id. Turn logging on finest to see what SQL is being executed.
One issue may be your initialValue=0, try removing or changing it to 1.