How to send Enum data inside request object - spring-boot

I have created an enum like the below to store static dropdown value.
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Days {
MONDAY(1,"Monday"), TUESDAY(2,"Tuesday"),THURSDAY(4,"Thursday"), FRIDAY(5,"Friday"), SATURDAY(6,"Saturday"),
SUNDAY(7,"Sunday");
private final Integer key;
private final String value;
Days(Integer key, String value) {
this.key = key;
this.value = value;
}
public Integer getKey() {
return key;
}
public String getValue() {
return value;
}
}
I have written a below endpoint to list enum data as a JSON response
#GetMapping("/getenums")
public List<Days> getenums() {
return Arrays.asList(Days.values());
}
which results in an array of JSON Objects like below
[{"key":1,"value":"Monday"},{"key":2,"value":"Tuesday"},{"key":4,"value":"Thursday"},{"key":5,"value":"Friday"},{"key":6,"value":"Saturday"},{"key":7,"value":"Sunday"}]
I have sample domain like below
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String name;
private Days day;
private Month month;
}
Below is my controller,
#PostMapping("/user")
public User createInstructor(#RequestBody User user) {
return userRepository.save(user);
}
While trying to save the user using the below request data getting bad request how can I resolve this
{
"name": "Pradeep",
"day": {
"key": 1,
"value": "Monday"
},
"month": "JUNE"
}

If you really want to return the complete enum object in your REST API then one solution could be adding a custom deserializer to it:
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Days {
MONDAY(1,"Monday"), TUESDAY(2,"Tuesday"),THURSDAY(4,"Thursday"), FRIDAY(5,"Friday"), SATURDAY(6,"Saturday"),
SUNDAY(7,"Sunday");
private final Integer key;
private final String value;
Days(Integer key, String value) {
this.key = key;
this.value = value;
}
public Integer getKey() {
return key;
}
public String getValue() {
return value;
}
#JsonCreator
public static Days fromObject(Map<String, Object> obj) {
if (obj != null && obj.containsKey("key") && obj.containsKey("value")) {
Integer key = obj.get("key");
String value = obj.get("value");
if (key != null) {
for (Days day : Days.values()) {
if (key.equals(e.getKey())) {
return day;
} else {
return null;
}
}
}
return null;
}
return null;
}
}
Additionally, you might want to tell Hibernate how to store your Enum in the database. You can use #Enumerated(EnumType.STRING) for that as follows:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String name;
#Enumerated(EnumType.STRING)
private Days day;
private Month month;
}
This will store it as a String. The other option is ORDINAL, which will persist the Enum as an Integer.

Related

Return the built Enum to JSON

I have an object and an enum for it. When I give away an object, I want my enum inside the object to be displayed as an object with the name and value attributes without using DTO, or to be partially used. I want json to build this object itself (enum with name and value), and I give only the object in which this enum is contained.
public enum MyType {
TT("Time Tu"), TD("Time dust");
MyType(String value) {
this.value = value;
}
private String value;
public String getValue() {
return value;
}
#Override
public String toString() {
return value;
}
Here is the DTO, it may be necessary (get/set/constructor auto generated by Intellij Idea)
public class MyTypeWrapper {
private String name;
private String value;
}
#Entity
public class MyObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
private String number;
........
private MyType myType;
........
}
Perhaps serialization/deserialization is needed? How to do it?
It should go something like this:
{
.....
myType: {
"name: "TT",
"value: "TD"
},
.....
}
Perhaps this is a piece of the solution? But I'm not sure that it will work, and it's not clear how to serialize
public enum MyType {
......
#JsonValue
private MyTypeWrapper getWrapper()
return new MyTypeWrapper(this.name, this.value)
}
......
}
This turned out to be the solution
public enum MyType {
......
#JsonValue
private MyTypeWrapper getWrapper()
return new MyTypeWrapper(this.name, this.value)
}
......
}

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.

JPA criteria builder equal method is not working as expected

In my case I'm trying to fetch some data by extracting a value from a json column in db. My code is as follows,
criteriaBuilder.equal(criteriaBuilder.function("JSON_EXTRACT", Boolean.class, root.get("result"), criteriaBuilder.literal("$.matched")), false);
Above code gives me an empty set of data. Also this is working fine in query console.
But,
criteriaBuilder.between(criteriaBuilder.function("JSON_EXTRACT", Double.class, root.get("result"), criteriaBuilder.literal("$.streaming_threshold")), 0.1, 0.9);
this between method is working fine. What could be the mistake here?
UPDATE
Boolean values are the values that I couldn't read. NOT INTEGERS. My JSON structure,
{
"status": "SUCCESS",
"request_id": "request_id",
"time_taken": 8454,
"matched": false,
"streaming_threshold": 0.5
}
I was not able to get it working with the raw boolean field. I converted the Boolean property on the object to String and vice-versa using #JsonSerialize and #JsonDeserialize and then persisted that as json and followed the same approach you did but now searching String.class as false instead of Boolean. My solution is as below:
Entity
#Entity
#Table(name = "json_container")
public class JsonContainer {
#Id
#GeneratedValue
#Type(type = "uuid-char")
private UUID id;
#Column(columnDefinition = "json", name = "json_data")
private String jsonData;
public UUID getId() {
return id;
}
public String getJsonData() {
return jsonData;
}
public void setJsonData(String jsonData) {
this.jsonData = jsonData;
}
public static class SampleDetails {
private String status;
private String requestId;
private Integer timeTaken;
#JsonSerialize(using = StringBooleanJsonSerializer.class)
#JsonDeserialize(using = StringBooleanJsonDeserializer.class)
private Boolean matched;
private Double streamingThreshold;
public SampleDetails() {
}
public SampleDetails(String status, String requestId, Integer timeTaken, Boolean matched, Double streamingThreshold) {
this.status = status;
this.requestId = requestId;
this.timeTaken = timeTaken;
this.matched = matched;
this.streamingThreshold = streamingThreshold;
}
public String getStatus() {
return status;
}
public String getRequestId() {
return requestId;
}
public Integer getTimeTaken() {
return timeTaken;
}
public Double getStreamingThreshold() {
return streamingThreshold;
}
public Boolean getMatched() {
return matched;
}
static class StringBooleanJsonSerializer extends JsonSerializer<Boolean> {
#Override
public void serialize(Boolean value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value != null && value ? value.toString() : "false");
}
}
static class StringBooleanJsonDeserializer extends JsonDeserializer<Boolean> {
#Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return Boolean.parseBoolean(p.getText());
} catch (RuntimeException e) {
return Boolean.FALSE;
}
}
}
}
Test Class
class MySQLJsonConverterTest {
#Autowired
private EntityManager entityManager;
#Rollback(false)
#Test
void testCustomJsonConverter() throws JsonProcessingException {
JsonContainer jsonContainer = new JsonContainer();
jsonContainer.setJsonData(
getAsJson(new JsonContainer.SampleDetails("success", "12344567", 8454, false, 0.1)));
entityManager.persist(jsonContainer);
Assertions.assertNotNull(jsonContainer.getId());
jsonContainer = new JsonContainer();
jsonContainer.setJsonData(
getAsJson(new JsonContainer.SampleDetails("success", "8989", 121, true, 0.5)));
entityManager.persist(jsonContainer);
Assertions.assertNotNull(jsonContainer.getId());
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JsonContainer> criteriaQuery = criteriaBuilder.createQuery(JsonContainer.class);
Root<JsonContainer> from = criteriaQuery.from(JsonContainer.class);
criteriaQuery.where(criteriaBuilder.equal(criteriaBuilder.function("JSON_EXTRACT", String.class, from.get("jsonData"),
criteriaBuilder.literal("$.matched")), "false"));
TypedQuery<JsonContainer> typedQuery = entityManager.createQuery(criteriaQuery);
List<JsonContainer> resultList = typedQuery.getResultList();
Assertions.assertEquals(1, resultList.size());
}
private String getAsJson(JsonContainer.SampleDetails sampleDetails) throws JsonProcessingException {
//var created so debugging is ez
String json = new ObjectMapper().writeValueAsString(sampleDetails);
return json;
}
}
Default JPA convert boolean to 0/1, so if serialize boolean to 0/1 into database, the equal query will be ok.

How to create a custom json object to columns value returned from my custom #Query

I have a query to My SQL database and I use Spring Boot to return it to Json format.
My problem is it only return value without key like:
[
[
"kermit",
6
]
]
I want it return like:
[
[
"name":"kermit",
"count" :6
]
]
I tried add Jackson Annotation jar file to project and use #JsonProperty in my entity model class:
#Entity
#Table(name = "act_id_membership", schema = "activiti", catalog = "")
#IdClass(ActIdMembershipEntityPK.class)
public class ActIdMembershipEntity {
#JsonProperty("name")
private String userId;
#JsonProperty("group")
private String groupId;
#Id
#Column(name = "USER_ID_")
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
#Id
#Column(name = "GROUP_ID_")
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActIdMembershipEntity that = (ActIdMembershipEntity) o;
return Objects.equals(userId, that.userId) &&
Objects.equals(groupId, that.groupId);
}
#Override
public int hashCode() {
return Objects.hash(userId, groupId);
}
}
But it still return without key. What I should do now? Please help me!
Thank you very much!
First, I'm agree with guy who commented that is not valid JSON format. You can see examples here https://json.org/example.html
Second, You need to create an object JSON which has fields needed for example:
public class UserStat es implements Serializable {
private String name;
private long count;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
}
And in your custom query. Based your return looks like on this way:
#Query("SELECT u.name, count(u) FROM User u")
public List<UserStat> findUserStat() ;

Spring merge - java.sql.SQLException: Invalid column index

I have a table that has 2 columns that create the primary key:
#Entity
#Table(name="DW.DW$SF$MONTHLY")
public class DWMonthly implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId DWMonthlyPK monthlyPK;
#Id
#Column(name="AID", insertable=false, updatable=false)
#IndexColumn(name="DW$SF$MONTHLY_PK")
private Long id;
#Column(name="CHNG_STATUS")
private BigDecimal chngStatus;
#Column(name="NAR")
private BigDecimal nar;
#Column(name="SF_AMID")
private String sfAmid;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="N_DATE", insertable=false, updatable=false)
#IndexColumn(name="DW$SF$MONTHLY_PK")
private Date nDate;
public DWMonthly() {}
public DWMonthly(Long id, String sfAmid, Date nDate) {
this.id = id;
this.sfAmid = sfAmid;
this.nDate = nDate;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public BigDecimal getChngStatus() {
return this.chngStatus;
}
public void setChngStatus(BigDecimal chngStatus) {
this.chngStatus = chngStatus;
}
public BigDecimal getNar() {
return this.nar;
}
public void setNar(BigDecimal nar) {
this.nar = nar;
}
public String getSfAmid() {
return this.sfAmid;
}
public void setSfAmid(String sfAmid) {
this.sfAmid = sfAmid;
}
public Date getNDate() {
return nDate;
}
public void setNarDate(Date nDate) {
this.nDate = nDate;
}
}
Embeddable table:
#Embeddable
public class DWMonthlyPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="AID")
private long aid;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="N_DATE")
private java.util.Date nDate;
public DWMonthlyPK() {
}
public long getAid() {
return this.aid;
}
public void setAid(long aid) {
this.aid = aid;
}
public java.util.Date getNDate() {
return this.narDate;
}
public void setNDate(java.util.Date nDate) {
this.nDate = nDate;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof DWMonthlyPK)) {
return false;
}
DWMonthlyPK castOther = (DWMonthlyPK)other;
return
(this.aid == castOther.aid)
&& this.nDate.equals(castOther.nDate);
}
public int hashCode() {
final int prime = 31;
int hash = 17;
hash = hash * prime + ((int) (this.aid ^ (this.aid >>> 32)));
hash = hash * prime + this.nDate.hashCode();
return hash;
}
}
Find the record and update:
#Repository(value="MonthlyNarDaoRepository")
#Import({JpaConfigurationImpl.class})
#Transactional(value="dwTransactionManager",readOnly = true, propagation=Propagation.REQUIRES_NEW)
public class MonthlyNarDaoImpl implements MonthlyNarDao {
#Override
#Transactional(value="dwTransactionManager")
public void findAndUpdateMonhtlyByAccountId(Map<DWAccount, UpsertResult> accountsLoadedResult) {
for (DWAccount dwAccount : accountsLoadedResult.keySet()){
try {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<DWMonthlyNar> criteriaQuery = criteriaBuilder.createQuery(DWMonthlyNar.class);
Root<DWMonthlyNar> root = criteriaQuery.from(DWMonthlyNar.class);
Predicate p = criteriaBuilder.conjunction();
p = criteriaBuilder.and(criteriaBuilder.equal(root.get("id"), dwAccount.getId()), criteriaBuilder.notEqual(root.get("nar").as(BigDecimal.class), new BigDecimal(0.0)), criteriaBuilder.isNull(root.get("sfAmid")));
criteriaQuery.select(root);
criteriaQuery.where(p);
for (DWMonthlyNar dwMonthlyNar : this.entityManager.createQuery(criteriaQuery).getResultList()){
if (dwMonthlyNar.getSfAmid()==null || !dwMonthlyNar.getSfAmid().equals(accountsLoadedResult.get(dwAccount).getId())){
dwMonthlyNar.setSfAmid(accountsLoadedResult.get(dwAccount).getId());
this.entityManager.merge(dwMonthlyNar);
}
}
} catch (Exception e) {
logger.error("MonthlyNarDaoImpl.findAndUpdateMonhtlyNarByAccountId(): " + e.getMessage());
}
}
}
}
Error:
Hibernate: update DW.DW$SF$MONTHLY set CHNG_STATUS=?, NAR=?,
SF_AMID=? where AID=? and NAR_DATE=? 16:04:58.864 [main] DEBUG
o.h.e.jdbc.spi.SqlExceptionHelper - Invalid column index [n/a]
java.sql.SQLException: Invalid column index

Resources