Dynamic MongoDB collection in spring boot - spring

I want to create a MongoDB collection for each month dynamically.
Example: viewLog_01_2018, viewLog_02_2018
#Document(collection = "#{viewLogRepositoryImpl.getCollectionName()}")
#CompoundIndexes({
#CompoundIndex(def = "{'viewer':1, 'viewed':1}", name = "viewerViewedIndex",unique=true)
})
public class ViewLog {
private Integer viewer;
private Integer viewed;
private Date time;
public Integer getViewer() {
return viewer;
}
public void setViewer(Integer viewer) {
this.viewer = viewer;
}
public Integer getViewed() {
return viewed;
}
public void setViewed(Integer viewed) {
this.viewed = viewed;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}
The implementation for the collection name is as follows:
#Repository
public class ViewLogRepositoryImpl implements ViewLogRepositoryCustom {
private String collectionName;
public ViewLogRepositoryImpl() {
CommonUtility common = new CommonUtility();
Pair<Integer, Integer> pair = common.getStartingEndingDateOfMonth();
setCollectionName("viewLog_"+pair.getFirst()+"_"+pair.getSecond());
}
#Override
public String getCollectionName() {
return collectionName;
}
#Override
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
}
On my each request, to save a document, I am setting the collection name as:
#Autowired
ViewLogRepository viewLogRepository;
public boolean createLog(int viewer, int viewed,String viewed_mmm, Date time){
CommonUtility common = new CommonUtility();
Pair<Integer, Integer> pair = common.getStartingEndingDateOfMonth();
viewLogRepository.setCollectionName("viewLog_"+pair.getFirst()+"_"+pair.getSecond());
ViewLog viewLog = new ViewLog();
viewLog.setViewer(viewer);
viewLog.setViewed(viewed);
viewLog.setTime(time);
ViewLog viewLog2 = viewLogRepository.save(viewLog);
return true;
}
The problem I am facing is that I when for the first time I up my service the mongo collection that is created has the unique attribute for the fields 'viewer' and 'viewed' but for any subsequent collection that is created dynamically, the document does not have the unique constraint and multiple entries of same viewer-viewed combination are able to be inserted.
Any help will be very much appreciated.

Related

Why might Room database IDs all show as 0 in a log/Toast message?

I'm trying to get the id of the last inserted object into a database using Room with Android. I can fetch the last object using an SQL query and can call other methods to get the various properties of that object which the user has set when saving the object. But getId() always returns 0. When I examine the table contents in Android Studio's app inspector, I can clearly see that Room is generating a unique primary key for each row, but I just can't get at it. Can anyone suggest what the problem might be?
Here's the Dao query:
#Query("SELECT * FROM gamebooks_table WHERE gamebookId=gamebookId ORDER BY gamebookId DESC LIMIT 1")
LiveData<Gamebook> getSingleGamebookByID();
And here's the annotated entity class:
#Entity(tableName = "gamebooks_table")
public class Gamebook {
#PrimaryKey(autoGenerate = true)
private long gamebookId;
private String gamebookName;
private String gamebookComment;
private String gamebookPublisher;
private float gamebookStarRating;
public Gamebook(String gamebookName, String gamebookComment, String gamebookPublisher, float gamebookStarRating) {
this.gamebookName = gamebookName;
this.gamebookComment = gamebookComment;
this.gamebookPublisher = gamebookPublisher;
this.gamebookStarRating = gamebookStarRating;
}
public long getGamebookId() {
return gamebookId;
}
public String getGamebookName() {
return gamebookName;
}
public String getGamebookComment() {
return gamebookComment;
}
public String getGamebookPublisher() {
return gamebookPublisher;
}
public float getGamebookStarRating(){
return gamebookStarRating;
}
public void setGamebookId(long gamebookId) {
this.gamebookId = gamebookId;
}
}
SOLVED
Finally sorted this by adding an Observer to my DAO method which returns a single gamebook. Within the Observer's onChanged() method, I can loop through all Gamebooks in the LiveData List (even though there's only one because I'm limiting it to one in the SQL query) and call getId() to get their respective IDs.
mainViewModel.getSingleGamebook().observe(this, new Observer<List<Gamebook>>() {
#Override
public void onChanged(List<Gamebook> gamebooks) {
int i=0;
for(Gamebook gamebook : gamebooks){
gamebookId= gamebook.getGamebookId();
Log.d(TAG, "Gamebook Name: "+gamebook.getGamebookName()+ " Database ID: " +gamebookId);
i++;
}
}
});
I believe that your issue is due to the only constructor being available not setting the id so the LiveData uses the default value of 0 for a long.
I'd suggest having a default constructor and thus all setters/getters and (optionally) using #Ignore annotation for one of the constructors..
without #Ignore you get warnings Gamebook.java:8: warning: There are multiple good constructors and Room will pick the no-arg constructor. You can use the #Ignore annotation to eliminate unwanted constructors. public class Gamebook {
e.g. :-
#Entity(tableName = "gamebooks_table")
public class Gamebook {
#PrimaryKey(autoGenerate = true)
private long gamebookId;
private String gamebookName;
private String gamebookComment;
private String gamebookPublisher;
private float gamebookStarRating;
public Gamebook(){} /*<<<<< ADDED */
#Ignore /*<<<<< ADDED - is not required - could be on the default constructor but not both*/
public Gamebook(String gamebookName, String gamebookComment, String gamebookPublisher, float gamebookStarRating) {
this.gamebookName = gamebookName;
this.gamebookComment = gamebookComment;
this.gamebookPublisher = gamebookPublisher;
this.gamebookStarRating = gamebookStarRating;
}
public long getGamebookId() {
return gamebookId;
}
public String getGamebookName() {
return gamebookName;
}
public String getGamebookComment() {
return gamebookComment;
}
public String getGamebookPublisher() {
return gamebookPublisher;
}
public float getGamebookStarRating(){
return gamebookStarRating;
}
public void setGamebookId(long gamebookId) {
this.gamebookId = gamebookId;
}
/* ADDED setters */
public void setGamebookName(String gamebookName) {
this.gamebookName = gamebookName;
}
public void setGamebookComment(String gamebookComment) {
this.gamebookComment = gamebookComment;
}
public void setGamebookPublisher(String gamebookPublisher) {
this.gamebookPublisher = gamebookPublisher;
}
public void setGamebookStarRating(float gamebookStarRating) {
this.gamebookStarRating = gamebookStarRating;
}
}
You also probably want to be able to pass the respective id to the getSingleGamebookByID, so you may wish to change this to:-
#Query("SELECT * FROM gamebooks_table WHERE gamebookId=:gamebookId /*<<<<< ADDED to use id passed */ ORDER BY gamebookId DESC LIMIT 1")
LiveData<Gamebook> getSingleGamebookByID(long gamebookId /*<<<<< ADDED to use id passed */);
you would probably want to remove the comments.
Note the LiveData aspect has not been tested and is conjecture.
Example
This example shows that room is fine with your original code but that the issues is on the LiveData/Viewmodel side :-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
GamebookDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* Note The Database has .allowMainThreadQueries */
db = TheDatabase.getInstance(this);
dao = db.getGamebookDao();
long gb1id = dao.insert(new Gamebook("Gamebook1","blah","Gamebook1 Publisher", 10.1F));
long gb2id = dao.insert(new Gamebook("Gamebook2","blah","Gamebook2 Publisher", 6.1F));
long gb3id = dao.insert(new Gamebook("Gamebook3","blah","Gamebook3 Publisher", 10.1F));
logGameBook(dao.getSingleGamebookByID());
logGameBook(dao.getSingleGamebookByID());
logGameBook(dao.getSingleGamebookByID());
/* Alternative that allows the ID to be specified */
logGameBook(dao.getSingleGamebookByIDAlternative(gb1id));
logGameBook(dao.getSingleGamebookByIDAlternative(gb2id));
logGameBook(dao.getSingleGamebookByIDAlternative(gb3id));
}
void logGameBook(Gamebook gb) {
Log.d("GAMEBOOKINFO","Gamebook is " + gb.getGamebookName() + " id is " + gb.getGamebookId());
}
}
The above uses your original code, the TheDatabase is a basic #Database annotated class BUT with .allowMainThreadQueries so it is run on the main thread.
The log, after running, includes:-
2022-03-12 08:16:12.556 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
2022-03-12 08:16:12.558 I/chatty: uid=10132(a.a.so71429144javaroomidreturnedaszero) identical 1 line
2022-03-12 08:16:12.561 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
2022-03-12 08:16:12.568 D/GAMEBOOKINFO: Gamebook is Gamebook1 id is 1
2022-03-12 08:16:12.572 D/GAMEBOOKINFO: Gamebook is Gamebook2 id is 2
2022-03-12 08:16:12.574 D/GAMEBOOKINFO: Gamebook is Gamebook3 id is 3
Note how the first just returns the same object and thus id.

How do I insert values of elements that are part of the EmbeddedId in JPA?

I have a case where I need to execute an insert statement via createNativeQuery. I have an entity list I'm looping through in order to set the properties accordingly from another bean class, and then persist that data to the oracle database.
The problem I am facing is persisting the data that is part of the embeddedId (item, loc, weekstart, type, forecastId, insertTS). I need to persist that data for the new records to be inserted into the database. When I try to set the values from the POJO bean to my set method for the properties of my entity bean, nothing happens. Below is my code for setting the values of the properties from the POJO bean to my entity bean, along with my persistence method and the insert query being executed:
Validation class where validation occurs beforehand (missing to get the point) that includes the setting of my entity properties from the POJO bean:
List <InsertPromoData> insertPromos = new ArrayList<InsertPromoData>();
promo.forEach(record -> {
if (record.getErrorList().size() == 0) {
rowsSuccessful++;
Util.writeSuccessToFile(templateCd, successFile, record, successFields);
try {
InsertPromoData insertData = new InsertPromoData();
insertData.getId().setItem(record.getItem());
insertData.getId().setLoc(record.getLoc());
insertData.getId().setWeekStart(record.getWeek_Start_Date());
insertData.setNumberOfWeeks(record.getNumber_Of_Weeks());
insertData.getId().setType(record.getType());
insertData.getId().setForecastId(record.getForecast_ID());
insertData.setQty(record.getUnits());
insertPromos.add(insertData);
}
catch (Exception e) {
logger.error("Error with setting insertPromolist from promo list values and the error is " + e.getMessage());
}
}
else {
if (rowsFailure == 0) {
Util.writeHeaderToFile(templateCd, errorFile);
}
rowsFailure++;
Util.writeErrorToFile(templateCd, errorFile, record, record.getErrorList());
}
});
errorFile.close();
successFile.close();
OracleImpl.insertPromoData(insertPromos);
POJO bean (promo is the variable representing this list of beans in validation class above):
public class PromoBean extends ErrorListBean
{
public String Item;
public String Loc;
public String Week_Start_Date;
public String Units;
public String Forecast_ID;
public String Type;
public String Number_Of_Weeks;
public String getItem() {
return Item;
}
public void setItem(String item) {
Item = item;
}
public String getLoc() {
return Loc;
}
public void setLoc(String loc) {
Loc = loc;
}
public String getWeek_Start_Date() {
return Week_Start_Date;
}
public void setWeek_Start_Date(String week_Start_Date) {
Week_Start_Date = week_Start_Date;
}
public String getNumber_Of_Weeks() {
return Number_Of_Weeks;
}
public void setNumber_Of_Weeks(String number_Of_Weeks) {
Number_Of_Weeks = number_Of_Weeks;
}
public String getType() {
return Type;
}
public void setType(String type) {
Type = type;
}
public String getForecast_ID() {
return Forecast_ID;
}
public void setForecast_ID(String forecast_ID) {
Forecast_ID = forecast_ID;
}
public String getUnits() {
return Units;
}
public void setUnits(String units) {
Units = units;
}
}
Embeddable class representing the composite primary key of the table:
#Embeddable
public class PromoID implements Serializable {
#Column(name = "ITEM")
private String item;
#Column(name = "LOC")
private String loc;
#Column(name = "WK_START")
private String weekStart;
#Column(name = "TYPE")
private String type;
#Column(name = "FCSTID")
private String forecastId;
#Column(name = "U_TIMESTAMP")
private String insertTS;
public PromoID() {
}
public PromoID (String item, String loc, String weekStart, String type, String forecastId, String insertTS) {
this.item = item;
this.loc = loc;
this.weekStart = weekStart;
this.type = type;
this.forecastId = forecastId;
this.insertTS = insertTS;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public String getWeekStart() {
return weekStart;
}
public void setWeekStart(String weekStart) {
this.weekStart = weekStart;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getForecastId() {
return forecastId;
}
public void setForecastId(String forecastId) {
this.forecastId = forecastId;
}
public String getInsertTS() {
return insertTS;
}
public void setInsertTS(String insertTS) {
this.insertTS = insertTS;
}
//hashcode and equals methods
Persistence Bean:
#Entity
#Table(name = "U_USER_PROMO")
public class InsertPromoData {
#EmbeddedId
private PromoID id;
#Column(name="NUMBER_OF_WEEKS")
String numberOfWeeks;
#Column(name="QTY")
String qty;
#Id
#AttributeOverrides(
{
#AttributeOverride(name = "item",column = #Column(name="ITEM")),
#AttributeOverride(name = "loc", column = #Column(name="LOC")),
#AttributeOverride(name = "weekStart", column = #Column(name="WK_START")),
#AttributeOverride(name = "type", column = #Column(name="TYPE")),
#AttributeOverride(name = "forecastId", column = #Column(name="FCSTID"))
}
)
public PromoID getId() {
return id;
}
public void setId(PromoID id) {
this.id = id;
}
public String getNumberOfWeeks() {
return numberOfWeeks;
}
public void setNumberOfWeeks(String numberOfWeeks) {
this.numberOfWeeks = numberOfWeeks;
}
public String getQty() {
return qty;
}
public void setQty(String qty) {
this.qty = qty;
}
}
DAO class method to execute the update (entitymanagerfactory emf already initialized):
public static void insertPromoData(List<InsertPromoData> insertData) {
logger.debug("Execution of method insertPromoData in Dao started");
System.out.println("Size of the insertData list is " + insertData.size());
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
System.out.println("Beginning transaction for insertPromoData");
Query query = em.createNativeQuery(env.getProperty("insertPromoUploadData"));
for (InsertPromoData promoData : insertData) {
query.setParameter("item", promoData.getId().getItem());
query.setParameter("location", promoData.getId().getLoc());
query.setParameter("wkStart", promoData.getId().getWeekStart());
query.setParameter("numberOfWeeks", promoData.getNumberOfWeeks());
query.setParameter("type", promoData.getId().getType());
query.setParameter("fcstId", promoData.getId().getForecastId());
query.setParameter("quantity", promoData.getQty());
query.executeUpdate();
}
em.getTransaction().commit();
}
catch(Exception e) {
logger.error("Exception in beginning transaction");
e.printStackTrace();
}
finally {
em.clear();
em.close();
}
logger.debug("Execution of method insertPromoData in Dao ended");
}
Query in properties file:
insertPromoUploadData = INSERT INTO {h-schema}U_USER_PROMO (ITEM, LOC, WK_START, NUMBER_OF_WEEKS, TYPE, FCSTID, QTY, U_TIMESTAMP) VALUES (:item, :location, TO_DATE(:wkStart,'MM DD YYYY'), :numberOfWeeks, :type, :fcstId, :quantity, SYSDATE)
My list size from my DAO class is returning as 0 once I begin the transaction and not sure why it is empty. Is there a reason that it is empty? I'm trying to persist each of the fields to the database (including the composite key fields) via insert query. Any help appreciated.
After looking into this for hours, I finally came to the conclusion that the simplest way to executeUpdate() without running into issues due to my current #EmbeddedId/#Embeddable logic was to change it to use #IdClass for my composite PK class, and annotate the fields from the PK in my entity with #Id. This allowed my data to be persisted to the database. Another slight difference was adding the insertTS in my entity class and annotating with #Id and generating getters/setters. This was necessary for JPA to recognize all the properties being referenced that I am wanting to persist, though I am persisting insertTS using SYSDATE function from the oracle DB instead of utilizing the get/set methods and setting to the current time from the java side.
I am sure there is a way to use #EmbeddedId/#Embeddable logic and be able to persist the fields that are part of the EmbeddedId, however, this I found to be a more simplistic way of doing it without further complexity in the code.

How can I add data to CRUD in Vaadin by using setItems for grid?

I'm going to add data to a CRUD component in Vaadin. It's an easy question here.
But the issue I got is that I cannot add data to the CRUD by first getting the grid object and then set its items to it.
Here is my Vaadin class. This class begins first to get data from a JPA Spring database. OK. That's works. And the data is transfered into a collection named crudData. Then the crudData is beings set to crud.getGrid().setItems(crudData); and that's not working. I assume that if I get the grid from the CRUD, then I can set the grid items as well too and then they will show up on the CRUD....but no...
#Data
public class StocksCrud {
private Crud<StockNames> crud;
private List<StockNames> crudData;
private StockNamesRepository stockNamesRepository;
private CrudEditor<StockNames> createStocksEditor() {
TextField stockName = new TextField("Name of the stock");
FormLayout form = new FormLayout(stockName);
Binder<StockNames> binder = new Binder<>(StockNames.class);
binder.bind(stockName, StockNames::getStockName, StockNames::setStockName);
return new BinderCrudEditor<>(binder, form);
}
public StocksCrud(StockNamesRepository stockNamesRepository) {
this.stockNamesRepository = stockNamesRepository;
// Fill the crud
crudData = new ArrayList<StockNames>();
for(StockNames stockName: stockNamesRepository.findAll()) {
crudData.add(new StockNames(stockName.getId(), stockName.getStockName()));
}
// Crate crud table
crud = new Crud<>(StockNames.class, createStocksEditor());
crud.getGrid().setItems(crudData); // This won't work
crud.addSaveListener(e -> saveStock(e.getItem()));
crud.addDeleteListener(e -> deleteStock(e.getItem()));
crud.getGrid().removeColumnByKey("id");
crud.addThemeVariants(CrudVariant.NO_BORDER);
}
private void deleteStock(StockNames stockNames) {
boolean exist = stockNamesRepository.existsBystockName(stockNames.getStockName());
if(exist == true) {
crudData.remove(stockNames);
stockNamesRepository.delete(stockNames);
}
}
private void saveStock(StockNames stockNames) {
System.out.println(stockNames == null);
System.out.println(stockNamesRepository == null);
boolean exist = stockNamesRepository.existsBystockName(stockNames.getStockName());
if(exist == false) {
crudData.add(stockNames);
stockNamesRepository.save(stockNames);
}
}
}
Here is my error output:
java.lang.ClassCastException: com.vaadin.flow.component.crud.CrudFilter cannot be cast to com.vaadin.flow.function.SerializablePredicate
I know that there is a way to set data to CRUD in Vaadin, by using a data provider class. But I don't want to use that. It's....to much code. I want to keep it clean and write less code in Java. Example here at the bottom: https://vaadin.com/components/vaadin-crud/java-examples
#Entity
#Data
#NoArgsConstructor
public class StockNames implements Cloneable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String stockName;
public StockNames(int id, String stockName) {
this.id = id;
this.stockName = stockName;
}
}
Update:
This is my code now
#Data
public class StocksCrud {
private Crud<StockNames> crud;
private List<StockNames> crudData;
private StockNamesRepository stockNamesRepository;
private CrudEditor<StockNames> createStocksEditor() {
TextField stockName = new TextField("Name of the stock");
FormLayout form = new FormLayout(stockName);
Binder<StockNames> binder = new Binder<>(StockNames.class);
binder.bind(stockName, StockNames::getStockName, StockNames::setStockName);
return new BinderCrudEditor<>(binder, form);
}
public StocksCrud(StockNamesRepository stockNamesRepository) {
this.stockNamesRepository = stockNamesRepository;
// Fill the crud
crudData = new ArrayList<StockNames>();
for(StockNames stockName: stockNamesRepository.findAll()) {
crudData.add(new StockNames(stockName.getId(), stockName.getStockName()));
}
// Create grid
Grid<StockNames> grid = new Grid<StockNames>();
grid.setItems(crudData);
// Crate crud table
crud = new Crud<>(StockNames.class, createStocksEditor());
crud.setGrid(grid);
crud.addSaveListener(e -> saveStock(e.getItem()));
crud.addDeleteListener(e -> deleteStock(e.getItem()));
//crud.getGrid().removeColumnByKey("id");
crud.addThemeVariants(CrudVariant.NO_BORDER);
}
private void deleteStock(StockNames stockNames) {
boolean exist = stockNamesRepository.existsBystockName(stockNames.getStockName());
if(exist == true) {
crudData.remove(stockNames);
stockNamesRepository.delete(stockNames);
}
}
private void saveStock(StockNames stockNames) {
System.out.println(stockNames == null);
System.out.println(stockNamesRepository == null);
boolean exist = stockNamesRepository.existsBystockName(stockNames.getStockName());
if(exist == false) {
crudData.add(stockNames);
stockNamesRepository.save(stockNames);
}
}
}
Update 2:
This gives an error.
// Create grid
Grid<StockNames> grid = new Grid<StockNames>();
StockNames s1 = new StockNames(1, "HELLO");
crudData.add(s1);
grid.setItems(crudData);
// Crate crud table
crud = new Crud<>(StockNames.class, createStocksEditor());
crud.setGrid(grid);
crud.addSaveListener(e -> saveStock(e.getItem()));
crud.addDeleteListener(e -> deleteStock(e.getItem()));
crud.getGrid().removeColumnByKey(grid.getColumns().get(0).getKey());
crud.addThemeVariants(CrudVariant.NO_BORDER);
The error is:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0'
What? I just added a object.
Assigning grid fixes the issue
Grid<StockNames> grid=new Grid<>(StockNames.class);
crud = new Crud<>(StockNames.class,grid, createStocksEditor());
In your code example you are relying on default implementation provided by Crud, thus CrudGrid is getting created. Its setDataProvider returns DataProvider<E,CrudFilter>, whereas Grid's DataProvider is of type: AbstractDataProvider<T, SerializablePredicate<T>> (This is because you are using ListDataProvider, which extends AbstractDataProvider<T, SerializablePredicate<T>>). This is what error states:
java.lang.ClassCastException: com.vaadin.flow.component.crud.CrudFilter cannot be cast to com.vaadin.flow.function.SerializablePredicate
So if you want to assign values via grid- you would first need to create one. Otherwise, as shown in the docs you could provide a custom dataprovider: PersonDataProvider
Update
This is an example code I am using. Adding a new item in Crud works, after I have added a no-args constructor to the bean:
import java.util.Random;
public class StockNames implements Cloneable{
Random rnd=new Random();
private int id;
private String stockName;
public StockNames(){
//You will an id generated automatically for you, but here is just an example
id=rnd.nextInt(12000);
}
public StockNames(int id, String stockName) {
this.id = id;
this.stockName = stockName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStockName() {
return stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
}
and the StockCrud class:
import com.vaadin.flow.component.crud.*;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.router.Route;
import java.util.ArrayList;
import java.util.List;
#Route("crudLayout")
public class StockCrud extends VerticalLayout {
private Crud<StockNames> crud;
private List<StockNames> crudData;
private CrudEditor<StockNames> createStocksEditor() {
TextField stockName = new TextField("Name of the stock");
FormLayout form = new FormLayout(stockName);
Binder<StockNames> binder = new Binder<>(StockNames.class);
binder.bind(stockName, StockNames::getStockName, StockNames::setStockName);
return new BinderCrudEditor<>(binder, form);
}
public StockCrud() {
// Fill the crud
crudData = new ArrayList<StockNames>();
for(int i=0;i<150;i++) {
crudData.add(new StockNames(i,"Name " + i));
}
// Crate crud table
Grid<StockNames> grid=new Grid<>(StockNames.class);
crud = new Crud<>(StockNames.class,grid, createStocksEditor());
//((CrudGrid )crud.getGrid()).setItems(crudData);
crud.getGrid().setItems(crudData); // This won't work
crud.addSaveListener(e -> saveStock(e.getItem()));
crud.addDeleteListener(e -> deleteStock(e.getItem()));
// crud.getGrid().removeColumnByKey("id");
crud.addThemeVariants(CrudVariant.NO_BORDER);
add(crud);
}
private void deleteStock(StockNames stockNames) {
// if(crudData.contains(stockNames)) {
crudData.remove(stockNames);
//}
}
private void saveStock(StockNames stockNames) {
System.out.println(stockNames == null);
if(!crudData.contains(stockNames)) {
crudData.add(stockNames);
}
}
}

JPA - Get values from fields in other entities in a Controller class

I have two entities: Room and Service. I also have a method in Logic class that i want to use in ServiceController class. Other classes are irrelevant.
room.getBeds() always returns 0, default int value.
Room entity:
#Entity
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int room_id;
private int beds;
private int number;
public Room() {
}
public Room(int beds, int number) {
this.beds = beds;
this.number = number;
}
public int getRoom_id() {
return room_id;
}
public void setRoom_id(int room_id) {
this.room_id = room_id;
}
public int getBeds() {
return beds;
}
public void setBeds(int beds) {
this.beds = beds;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
Service entity:
#Entity
public class Service {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int service_id;
#JsonFormat(pattern = "dd/MM/yyyy", timezone = "Europe/Berlin")
private Date arrival_at;
#JsonFormat(pattern = "dd/MM/yyyy", timezone = "Europe/Berlin")
private Date departure_at;
private int meals;
private int guest_id;
private double price;
public int getService_id() {
return service_id;
}
public void setService_id(int service_id) {
this.service_id = service_id;
}
public Date getArrival_at() {
return arrival_at;
}
public void setArrival_at(Date arrival_at) {
this.arrival_at = arrival_at;
}
public Date getDeparture_at() {
return departure_at;
}
public void setDeparture_at(Date departure_at) {
this.departure_at = departure_at;
}
public int getMeals() {
return meals;
}
public void setMeals(int meals) {
this.meals = meals;
}
public int getGuest_id() {
return guest_id;
}
public void setGuest_id(int guest_id) {
this.guest_id = guest_id;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
Service controller:
#RestController
public class ServiceController {
#Autowired
ServiceRepository serviceRepository;
private Logic logic = new Logic();
private Room room = new Room();
//CREATE
#PostMapping("/services")
public int createService(#RequestBody Service service) {
service.setPrice(logic.calculatePrice(service.getArrival_at(), service.getDeparture_at(), room.getBeds(), service.getMeals()));
return serviceRepository.save(service);
}
}
How to get the correct value from room.getBeds() getter?
Lerius you are just making a new object of Room Entity and are not populating any values from your Database to that object
that is the reason why it is taking room.getBeds(); as 0(i.e default value for int)
Try Populating DB values to your object after that you will be able to get the value of getBeds();

Rest Json Jackson Mapper Custom Object Mapper

I am having an issue with the Jackson Json mapper which I can't figure out how to solve.
I am having a Spring MVC Rest application and the endpoints are converted to Json using Jackson.
Some of the result objects contain a type that I want to tamper with before it gets converted.
More specifically, a result object could look like this.
ResultObject
- getDoubleMap() : DoubleMap
- getDoubleEntries() : List<DoubleEntry>
- toMap() : Map<String, Double>
What I want to do is to not have Jackson convert the DoubleMap instance but much rather override it like this
Object someJacksonMapInterceptor(Object object) {
if(object instanceof DoubleMap) {
return ((DoubleMap) object).toMap();
}
return object;
}
I have tortured google quite a while now and not a simple solution. Hope someone can advise.
Many thanks in advance.
In one application, we are custom-deserealizing date, probably you can use it for your custom deserealization.
public class VitalSign {
public static final String DATE_FORMAT1 = "yyyy-MM-dd'T'HH:mm:ssZ";
public static final String DATE_FORMAT2 = "yyyy-MM-dd'T'HH:mm:ss";
//public static final String DATE_FORMAT3 = "yyyy-MM-dd'T'HH:mm:ssTDZ";
public static final String DATE_FORMAT4 = "MMM dd, yyyy h:mm:ss aa";
#NotNull
#Column(name = "observed")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(style = "M-")
#JsonDeserialize(using = CustomJsonDateDeserializer.class)
private Date timestamp;
public static class CustomJsonDateDeserializer extends JsonDeserializer<Date> {
public CustomJsonDateDeserializer() {
super();
}
#Override
public Date deserialize(JsonParser jsonparser, DeserializationContext deserializationcontext) throws IOException, JsonProcessingException {
SimpleDateFormat[] formats = { new SimpleDateFormat(DATE_FORMAT1), new SimpleDateFormat(DATE_FORMAT2), new SimpleDateFormat(DATE_FORMAT4, Locale.US) };
String date = jsonparser.getText();
for (SimpleDateFormat format : formats) {
try {
return format.parse(date);
} catch (ParseException e) {
}
}
throw new RuntimeException("Unparseable date " + date);
}
}
}
For serializing, you can just annotate your toMap() method with #JsonValue. For deserializing, if you have a static factory to create a DoubleMap from a Map<String, Double>, you can just annotate that with #JsonCreator.
private final ObjectMapper mapper = new ObjectMapper();
#Test
public void serialize_doublemap() throws Exception {
DoubleMap map = new DoubleMap();
map.put("red", 0.5);
map.put("orange", 0.7);
assertThat(mapper.writeValueAsString(map), equivalentTo("{ red: 0.5, orange: 0.7 }"));
}
#Test
public void deserialize_doublemap() throws Exception {
assertThat(mapper.readValue("{ \"red\": 0.5, \"orange\": 0.7 }", DoubleMap.class).toMap(),
equalTo(ImmutableMap.of("red", 0.5, "orange", 0.7)));
}
public static class DoubleMap {
public List<DoubleEntry> entries = new ArrayList<>();
public void put(String label, double value) {
entries.add(new DoubleEntry(label, value));
}
#JsonCreator
public static DoubleMap fromJson(Map<String, Double> input) {
DoubleMap map = new DoubleMap();
input.forEach(map::put);
return map;
}
public List<DoubleEntry> getDoubleEntries() {
return entries;
}
#JsonValue
public Map<String, Double> toMap() {
return entries.stream().collect(Collectors.toMap(e -> e.label, e -> e.value));
}
}
public static final class DoubleEntry {
public final String label;
public final double value;
public DoubleEntry(String label, double value) {
this.label = label;
this.value = value;
}
}

Resources