Spring-Batch: Item writer for Parent-Child relationship - spring-batch

I have written a item processor which returns the list of Objects. This object needs to be split into 2 data base table (One parent and a child). One header row and for this corresponding header ID we have child rows associated in child table. I have used ListUnpackingItemWriter example to solve list problem. I have used CompositeItemWriter to split the result into 2 writer, Now I need to split each one for header and child table. Now each writer has same number of rows. IS there a better way to do it? Solve both Table Primary key problem. I need example write custom item writer which does validation before insert. Thanking you in advance.
Below is the code
public JdbcBatchItemWriter<T> myWriter() {
JdbcBatchItemWriter<T> myWriter = new JdbcBatchItemWriter<T>();
myWriter.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<T>());
myWriter.setSql("INSERT INTO Parent table( colums) values ( values )");
myWriter.setDataSource(dataSource);
myWriter.afterPropertiesSet();
return myWriter;
}
public JdbcBatchItemWriter<T> myOtherWriter() {
JdbcBatchItemWriter<T> myWriter = new JdbcBatchItemWriter<T>(); 1
myWriter.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<T>());
myWriter.setSql("INSERT INTO child table( colums) values ( values )");
myWriter.setDataSource(dataSource);
myWriter.afterPropertiesSet();
return myWriter;
}
public CompositeItemWriter<T> compositeItemWriter() {
CompositeItemWriter<T> writer = new CompositeItemWriter<T>();
writer.setDelegates(Arrays.asList(myWriter(),myOtherWriter()));
return writer;
}
```

If the composite writer does not work for you, then can you use a custom writer, something like:
import java.util.List;
import javax.sql.DataSource;
import org.springframework.batch.item.ItemWriter;
import org.springframework.jdbc.core.JdbcTemplate;
public class MyParentChildWriter implements ItemWriter<Object> {
private JdbcTemplate jdbcTemplate;
public MyParentChildWriter(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
public void write(List<?> items) {
for (Object item : items) {
// get parent/child values from item and use them in the query as needed
jdbcTemplate.update("INSERT INTO Parent table( colums) values ( values )");
jdbcTemplate.update("INSERT INTO child table( colums) values ( values )");
}
}
}
Note that all update statements will be executed in a single transaction as explained in the Chunk-oriented Processing section.

Related

Eloquent hasMany with hasMany and a join in the middle

I have this database structure
orders ====► order_items ====► order_item_meta
║ |
║ |
▼ ▼
order_meta products
The relations are orders hasMany order_items which hasManyThrough order_item_meta, orders also hasMany order_meta.
In addition, the order_items/product_id needs to be joined with the products table.
I have the order_id and I am trying to get the whole data in one call. But I have a weird issue. This is the current code:
$orders = Orders::
with([
'order_items' => function($q) { //#1
$q->leftJoin('products','order_items.product_id', '=', 'products.id');
}
])
->with(['order_items.orderitem_meta']) //#2
->with(['order_meta']); //#3
It seems that with#1 and with#2 are interfering with each other.
Case1: If I do with#1+with#3, I am able to see in the result the data from the product table + the data from order_items, but not the data from order_item_meta.
Case2: If I do with#2+with#3, I am able to see in the result the data from the from order_items + data from order_item_meta, but not from the product table.
In both cases data from with#3 is ok.
But if I do all three together (with#1+with#2+with3) I get the same results as case1. data from order_item_meta is missing.
Orders.php
class Orders extends Model
{
public function order_items()
{
return $this->hasMany('App\OrderItem','order_id','id'); //'foreign_key', 'local_key'
}
public function order_meta()
{
return $this->hasMany('App\OrderMeta','order_id','id'); //'foreign_key', 'local_key'
}
public function orderitem_meta()
{
return $this->hasManyThrough(
'App\OrderItem',
'App\OrderItemMeta',
'order_item_id', // Foreign key on order_itemmeta table...
'order_id', // Foreign key on order_item table...
'id', // Local key on order_item table...
'id' // Local key on order_itemmeta table...
);
}
}
OrderItem.php
class OrderItem extends Model
{
public function order()
{
return $this->belongsTo('App\Orders');
}
public function orderitem_meta()
{
return $this->hasMany('App\OrderItemMeta','order_item_id','id'); //'foreign_key', 'local_key'
}
}
OrderItemMeta.php
class OrderItemMeta extends Model
{
protected $table = 'order_itemmeta';
public function orderitem()
{
return $this->belongsTo('App\OrderItem');
}
}
What is the correct way to do this query?
I solved it by adding a relationship between the order_items and the products:
in OrderItem.php
public function product()
{
return $this->hasOne('App\Products','id','product_id'); //'foreign_key', 'local_key'
}
then the query becomes this:
$orders = Orders::
with(['order_items.orderitem_meta','order_items.product','order_meta']);
and it works

custom item writer to write to database using list of list which contains hashmap

i am a new to spring batch and my requirement is to read a dynamic excel sheet and insert into database.I am able to read excel and pass it to writer but only last record in excel sheet gets inserted into database. Here is my code for item writer
#Bean
public ItemWriter<List<LinkedHashMap<String, String>>> tempwrite() {
JdbcBatchItemWriter<List<LinkedHashMap<String, String>>> databaseItemWriter = new JdbcBatchItemWriter<>();
databaseItemWriter.setDataSource(dataSource);
databaseItemWriter.setSql("insert into table values(next value for seq_table,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
ItemPreparedStatementSetter<List<LinkedHashMap<String, String>>> valueSetter =
new databasePsSetter();
databaseItemWriter.setItemPreparedStatementSetter(valueSetter);
return databaseItemWriter;
}
and below is my prepared statement setter class
public class databasePsSetter implements ItemPreparedStatementSetter<List<LinkedHashMap<String, String>>> {
#Override
public void setValues(List<LinkedHashMap<String, String>> item, PreparedStatement ps) throws SQLException {
int columnNumber=1;
for(LinkedHashMap<String, String> row:item){
columnNumber=1;
for (Map.Entry<String, String> entry : row.entrySet()) {
ps.setString(columnNumber, entry.getValue());
columnNumber++;
}
}
}
}
I have seen many examples but all of them is using a dto class but i am not sure whether this is the correct way of implementation for list of list which contains hashmap

How to populate the flex table in GWT from the backend

I have to pull out values from backend and fill it as rows in the flex table. I am not using static data, so I cannot use:
table.setText(0, 0, "Name");
table.setText(0, 1,"Birthdate");
table.setText(0, 2, "Address");
How can I pull out data and fill it as rows in flex table. Or should I use a Grid table for the same?
What's about something like this. In another class you could load your data from backend and create new Car Objekts and pass them to the MyCarFlexTable class.
class MyCarFlexTable extends FlexTable {
private int rowNumber;
private List<Car> cars;
public MyCarFlexTable(List<Car> cars) {
this.cars = cars;
initTable();
}
private void initTable() {
for(Car c : cars) {
// define which data is printed in which column
setText(rowNumber, 0, c.getId()); // Id of Car
setWidget(rowNumber, 1, new Label(c.getName())); // Name of Car
// add more Columns
rowNumber++;
}
}
}

Can OpenJPA be used to reverse map a database view?

I have database views that have a column that uniquely identifies each row in the view. This column could be used as the primary key even though the view doesn't have a primary key in its definition (DDL) because it's a view.
OpenJPA is refusing to map the views to Java POJOs because there is no primary key.
I have a list of views and primary keys and I have a ReverseCustomizer. Is it possible I can give OpenJPA the column/field to be used as the primary key / id for each view / class?
Currently, the reverse mapping tool calls unmappedTable for each view and I'd like to tell the reverse mapper to do the mapping with the primary key I provide.
Here's something to help you get started.
public class MyDBReverseCustomizer implements ReverseCustomizer {
private ReverseMappingTool rmt;
#Override
public void setTool(ReverseMappingTool rmt) {
this.rmt = rmt;
}
#Override
public boolean unmappedTable(Table table) {
// this method is called to give this class an opportunity to map the
// table which would not be mapped otherwise. Returning false says
// the table wasn't mapped here.
//Class klass = rmt.generateClass(table.getIdentifier().getName(), null);
String packageName = rmt.getPackageName();
String tableName = table.getIdentifier().getName();
String className = NameConverters.convertTableName(tableName);
Class klass = rmt.generateClass(packageName+"."+className, null);
ClassMapping cls = rmt.newClassMapping(klass, table);
Column pk = null;
for ( Column column : table.getColumns() ) {
String columnName = column.getIdentifier().getName();
String fieldName = rmt.getFieldName(columnName, cls);
Class type = rmt.getFieldType(column, false);
FieldMapping field = cls.addDeclaredFieldMapping(fieldName, type);
field.setExplicit(true);
field.setColumns(new Column[]{column});
// TODO: set the appropriate strategy for non-primitive types.
field.setStrategy(new PrimitiveFieldStrategy(), null);
if ("MODEL_VIEW".equals(tableName) && "MODEL_ID".equals(columnName)) {
pk = column;
field.setPrimaryKey(true);
}
customize(field);
}
//cls.setPrimaryKeyColumns(new Column[]{pk});
cls.setObjectIdType(null, false);
cls.setIdentityType(ClassMapping.ID_DATASTORE);
cls.setStrategy(new FullClassStrategy(), null);
return true;
}
...
}

Custom GWT Tree table with TreeItem and FlexTable

GWT does not contain TreeTable but my project requirement with tree table. so I created it by using Tree and FlexTable.
Here Bean class: Item
class Item
{
ItemType itemType; //Parent,Child Enum type
long parentId;
long amount;
String itemName;
String qty;
String rate;
//Setter / Getter Methods
}
This is custom TreeItem class which has relation between parent Item & Child Item. I have Created one row By using TreeItemNode.
public class TreeItemNode extends TreeItem {
private FlexTable table; //this will use to access child
private FlexTable parentTable; //This will use to access parent of particular child.
//This constructor create parent node
public TreeItemNode(FlexTable parentTable) {
setWidget(parentTable);
this.parentTable=parentTable;
setState(true);
}
//The method used to add Child items in parent
public TreeItem addItem(Widget widget) {
this.table=(FlexTable) widget;
TreeItem ret = new TreeItem(widget);
addItem(ret);
return ret;
}
}
Using Item List, This method is creating the tree which has parent and child node relations.
class CustomTreeTable extends Tree
{
public void createTreeTable() {
//This map store parent id and tree item object for letter fetch the parent node and add child node in it
treeItemMap = new HashMap<Long, BOQTreeItem>();
for (Item item : itemList) {
if (item.getParentItemId() == 0) {
//Create new parent node
TreeItemNode itemTreeItem = new TreeItemNode(addChildItem(item,
new FlexTable(), true),
"gwt-TreeNode");
treeItemMap.put(item.getId(), treeItem);
addItem(itemTreeItem);
} else {
//Add child node
if (treeItemMap.containsKey(item.getParentItemId())
&& treeItemMap.get(item.getParentItemId()) != null) {
FlexTable dataFlextable = treeItemMap.get(
item.getParentItemId()).getTable();
if (dataFlextable == null) {
dataFlextable = new FlexTable();
treeItemMap.get(item.getParentItemId()).setTable(
dataFlextable);
}
FlexTable treeTable = addChildItem(item, dataFlextable,
false);
treeItemMap.get(item.getParentItemId()).addItem(treeTable);
}
}
}
}
}
Using this my table is as below. + is my tree node open/ close like:
ItemName qty rate amount
+ item1 11 1 11 //parent item
---------------------------
- item2 11 2 22
--------------------------------
SubItemName qty rate amount //child item
subItem1 5 2 10
subItem2 8 5 40
------------------------------------
The problem is when I want calculate qty* rate in one raw (child item) than I can do using row index but if I want to do calculate all the child item & show in parent row. then logic become so complex. so I just want to know the logic which I implemented like store the instance in map and managed. is it proper? I am going in right direction or wrong.
Maybe using a DataGrid Widget will be simpler for what you are trying to achieve.
Look at the GWT ShowCase : here
On the DataGrid, you have childs under parent row displaying when you click on ShowFriends link.
Hope it helps. :)