Fix string contraints on JPA entity attribute - spring

I am new in JPA,
I want to set only specific fix department names to attribute in entity as a fix string as constraints.I.e default values to attributes.
How to set it?

I think the best option is to use enumerated as indicated by Dinesh Dontha, try this:
Entity
#Entity
public class MyEntity implements Serializable(){
private MyEnum attribute;
}
Enum
public enum MyEnum {
NAME1("N1")
private String shortName;
private MyEnum(String shortName) {
this.shortName = shortName;
}
public String getShortName() {
return shortName;
}
public static MyEnum fromShortName(String shortName) {
switch (shortName) {
case "N1":
return NacionalidadEnum.NAME1;
default:
throw new IllegalArgumentException("ShortName [" + shortName
+ "] not supported.");
}
}
}
Converter
#Converter(autoApply = true)
public class MyEntityEnumConverter implements AttributeConverter<MyEnum, String> {
#Override
public String convertToDatabaseColumn(MyEnum myEnum) {
return myEnum.getShortName();
}
#Override
public MyEnum convertToEntityAttribute(String dbData) {
return MyEnum.fromShortName(dbData);
}
}

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)
}
......
}

enforce enum serialization with a spring jpa projection?

Say I have the following JPA entity, with an enum field ON/OFF mapped to an SQL enum("on", "off").
#Entity
public class Process {
#Id
private Long Id;
#Convert(converter = StatusConverter.class)
private Status status;
// getter/setter omitted
}
public enum Status {
ON("on"),
OFF("off");
private final String status;
Status(String status) {
this.status = status;
}
// JSON (de)serialization
#JsonCreator
public static Status decode(String status) {
return valueOf(status.toUpperCase());
}
#JsonValue
public getStatus() {
return status;
}
// DAO layer conversion
public String toDatabaseColumn() {
return this.name().toLowerCase();
}
}
#Converter
public class StatusConverter implements AttributeConverter<Status, String> {
#Override
public String convertToDatabaseColumn(Status attribute) {
return attribute.toDatabaseColumn();
}
#Override
public Status convertToEntityAttribute(String dbData) {
return Status.decode(dbData);
}
}
// Spring JPA projection
public interface ProcessSummary {
String getStatus();
}
// a minimalist JPA repository
public interface ProcessRepository extends Repository<Process, Long> {
<T> T findById(Long id, Class<T> type;
}
If I use repository.findById(1L, Process.class) in a REST controller, both the DAO layer conversion and the JSON serialization work as expected :
my database record has its status set to on
it is mapped to the Java Status.ON
the entity is serialized as
{
"status" : "on"
}
But if I use repository.findById(1L, ProcessSummary.class) instead, the entity is serialized as
{
"status" : "ON"
}
How can I get the same result when using a projection as target type? Is it possible with a projection, or should I try something else (a DTO class maybe)?
Sorry folks, it was just me and a textbook case of of PEBKAC :)
The getStatus() method in the interface MUST return a Status, not a String.
public interface ProcessSummary {
String getStatus();
}
does what it's asked: converts the enum to a String, hence Status.ON is serialized as "ON", while
public interface ProcessSummary {
Status getStatus();
}
indeed uses the #JsonValue annotated method and serializes Status.ON as "on".

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.

Custom Source presence checking method name in MapStruct

is it posible to generate a custom "presence checking" method name, being a method of the property itself rather the owning object?
I know I can use hasProperty() methods to check for presence of a value...
https://mapstruct.org/documentation/stable/reference/html/#source-presence-check
but with Optional or JsonNullable (from OpenApi nonullable) that checking method is on the property itself, not on the owning object... :-(
I can map JsonNullable or Optional easyly 'using' or extending a simple custom Mapper
#Mapper
public class JsonNullableMapper {
public <T> T fromJsonNullable(final JsonNullable<T> jsonNullable) {
return jsonNullable.orElse(null);
}
public <T> JsonNullable<T> asJsonNullable(final T nullable) {
return nullable != null ? JsonNullable.of(nullable) : JsonNullable.undefined();
}
}
what I would like to achieve is something like this as "presence check":
if(source.getProperty().isPresent()) {
target.set(customMapper.map(source.getProperty()));
}
Any one found a solution for this?
Thanks and regards
I have managed to implement custom lombok extension which generates "presence checknig" methods.
Here is an example project. In short I added #PresenceChecker annotation and implemented Lombok Javac Annotation handler.
It's possible to use it together with other Lombok annotations:
#Getter
#Setter
public class User {
private String name;
}
#Getter
#Setter
#PresenceChecker
public class UserUpdateDto {
private String name;
}
//MapStruct Mapper interface declaration
#Mapper
public interface UserMapper {
void updateUser(UserUpdateDto dto, #MappingTarget User user);
}
Generated code:
public class User {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class UserUpdateDto {
private boolean hasName;
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
this.hasName = true;
}
public boolean hasName() {
return this.hasName;
}
}
//MapStruct Mapper implementation
public class UserMapperImpl implements UserMapper {
#Override
public void updateUser(UserUpdateDto dto, User user) {
if ( dto == null ) {
return;
}
if ( dto.hasName() ) {
user.setName( dto.getName() );
}
}
}
The answer is unfortunately a straight no.
It is not possible in the current version of MapStruct (1.3.1final) and its not on the shortlist for 1.4.0. You could open up an issue on the git repo of MapStruct as feature request.

Playframework! Using #select for a Enum property

I'm trying to create a #select input for a enum field. Everything works fine until the form is submitted. It fails with a weird validation error -> "error.invalid"
here's my code
Enum class
package model;
...
public enum UserType {
UserType_Admin("Administrator"), UserType_Monitor("Monitor"), UserType_Audit("Audit");
private String desc;
private UserType(String desc) {
this.desc = desc;
}
#Override
public String toString() {
return Messages.get(desc);
}
public String getLabel() {
return toString();
}
public String getKey() {
return super.toString();
}
public static Map<String, String> options() {
LinkedHashMap<String, String> options = new LinkedHashMap<String, String>();
for (UserType ut : UserType.values()) {
Integer o = ut.ordinal();
options.put(o.toString(), ut.desc);
}
return options;
}
}
My Entity
#Entity
public class User extends Model {
...
#Id
public Long userID;
public UserType user_type;
}
Scala template
#form(routes.Users.save(userID)) {
#select(
userForm("user_type"),
options(model.UserType.options),
'_label -> Messages("UserType"), '_default -> Messages("choose_user_type"),
'_showConstraints -> true
)
}
on the controller the Save method:
public static Result save(Long userID) {
Form<User> userForm = form(User.class).bindFromRequest();
if (userForm.hasErrors()) { <- here it says that has errors
return badRequest(useredit.render(new Session(session()), userID,
userForm, new User()));
}
...
}
if I inspect the userForm variable, I get:
Form(of=class model.User, data={user_type=0}, value=None,
errors={user_type=[ValidationError(user_type,error.invalid,[])]})
The field user_type has the correct value, 0 if I choose the first item, 1 for the second, etc.
Screenshot
Anyone has a clue or a workaround for this? Maybe disable validation for this field? Tks guys

Resources