I have two tables notification and message.
Message.java
...
...
#Table(name = "message",
uniqueConstraints = {#UniqueConstraint(name = "UniqueMessage",
columnNames = { "message_id" })})
public class Message implements Serializable {
#Id
#Column(name = “message_id")
private int messageId;
#Column(name = "description")
private String description;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "start_time")
private Date startTime;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "end_time")
private Date endTime;
#PrePersist
#PreUpdate
public void generateMessageId(){
this.messageId = Math.abs(Objects.hash(this.getDescription(),
this.getStartTime().getTime() / 1000));
}
My use case is -- I will be creating a message with certain description and start time. If I get same message again, I should be able to update the endTime. MessageId is calculated separately under method generateMessageId as this will act as an identifier to find if message has already been saved. If yes, I will update the message. It works well for first message but when I try to save again with updated endTime, I get
ERROR: duplicate key value violates unique constraint “message_pkey”
Steps:
I tried:
Message m1 = new Message();
m1.setDescription(“fake”);
m1.setStartTime(“2022-01-03T12:05:00”)
messageRepository.save(m1);
This works well. I can see a row in database with
Message_id description start_time end_time
46536723 fake 2022-01-03T12:05:00 null
Message m2 = new Message();
m2.setDescription(“fake”);
m2.setStartTime(“2022-01-03T12:05:00”);
m2.setEndTime(“2022-01-05T12:00:00)
messageRepository.save(m2);
I get ERROR: duplicate key value violates unique constraint error.
Isn’t jpa should find the existing id and do an update to the row instead of inserting?
Please suggest if this method looks like a work-around.
You are trying to update the message endTime, First time, it works because there is no entry of that message now when you are trying to update but you are not setting the id by default id is zero and if Id is zero it will try to save the message instead of updating.
For resolving this issue you can check (by finding the message by message because the message is unique) if that message is present then update the endTime.
Related
Well,
Im trying to validate a NotNull attribute
#Temporal(TemporalType.DATE)
#Column(name = "DATA_PAGAMENTO")
#NotNull
private Date dataPagamento;
So I created a custom message instead showing the default one
ValidationMessages.properties
javax.validation.constraints.NotNull.message={0} cannot be null!
this {0} would suppose to bring dataPagamento and show something like:
"dataPagamento cannot be null!"
but it keeps showing this:
{
"Validations": [
"{0} cannot be null!"
]
}
i am using thymeleaf and spring boot in my project and i need to save date and
time object in mySql database, this is how i have validated my entity class
#NotNull
#Column(name = "sdate")
#DateTimeFormat(iso = ISO.DATE)
private Date sdate; //represents start Date - i am passing 2014-01-01- this works fine.
#NotNull
#Column(name = "stime")
#DateTimeFormat(iso = ISO.TIME)
private Date stime; //represent start time - i am passing 12:10:20.444
when i am trying to get the values from thymleaf form to the controller i cant get a valid class it contain errors. is there something i am doing wrong with the validation part. how can i get a valid input from the form.
public String save(#ModelAttribute("travel") #Valid Travel travel,BindingResult result){
if (result.hasErrors()) {
for (Object element : result.getAllErrors()) {
System.out.println(element.toString());
}
}
error message :
Field error in object 'travel' on field 'stime': rejected value [12:10:20.444]
According to spring document for DateTimeFormat.ISO.TIME, the right format for ISO.TIME is "12:10:20.444+00:00", where the "+00:00" part is the time zone offset (suppose that there is no offset for your time zone).
So you need to reformat your input to include the time zone offset in your time string.
I tried to update a row in my DB using Ebean in my Play! Framework program.
Here is the class of the entity I would like to update.
Transaction.java
#Entity
#Table(name = "transactions")
public class Transaction extends Model{
#Id
#GeneratedValue
public int id;
#OneToOne
#JoinColumn(name = "car_fk")
public Car car;
#OneToOne
#JoinColumn(name = "user_lender_fk")
public User user;
#Version
public Timestamp from_date;
#Version
public Timestamp to_date;
public boolean availability; // true -> Available.
public String status;
}
And here is the metho I use to update it:
Transaction transaction = new Transaction();
transaction.car = concernedCars.get(i);
transaction.user = currentUser;
transaction.from_date = Tools.StringAndroidToTimestamp(dateFrom);
transaction.to_date = Tools.StringAndroidToTimestamp(dateTo);
transaction.status = Constants.WAITING_FOR_ANSWER;
try{
Ebean.update(transaction);
}catch(OptimisticLockException e){
Logger.info(e.toString());
}
And if necessary, my method to convert a String to Timestamp:
public static Timestamp StringAndroidToTimestamp(String s){
String toConvert = s.substring(0, s.length()-2);
Logger.info("ToConvert = "+toConvert);
Timestamp timestamp = null;
try{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parsedDate = dateFormat.parse(toConvert);
timestamp = new Timestamp(parsedDate.getTime());
}catch(Exception e){
Logger.info("Exception date = " +e.toString());
}
return timestamp;
}
Of course, I get the fabulous error:
javax.persistence.OptimisticLockException: Data has changed. updated
[0] rows sql
What did I do wrong?
There are few ways you can handle this.
1) Use #EntityConcurrencyMode(ConcurrencyMode.NONE) before class name
2) Use raw update query.(preferred)
I was facing too much problem because of the same ebean.update throwing optimistic lock exception, finally I did raw update query and it worked for me.
I can see some problems from here:
You are using two Version fields. I don't know if that is alright to Ebean but usually one is enough. Also I think Ebean will manage those fields by itself, so it may be better for you to specify a version field that you don't want to use (an int to serve as a counter or a timestamp of last change);
You are calling update but it seems you are really creating a new transaction, so you should be using Ebean.save
Regarding the error, that exception is thrown when you try to update a record that has changed between the time you loaded it and the update. To find out that the record has changed, Ebean uses the values from the Version columns.
So the update in your code would generate a SQL similar to this:
UPDATE transactions
SET car_fk=<SOME_VAL>, ...
WHERE id=null AND from_date=<OTHER_VAL> AND to_date=<ANOTHER_VAL>
which wouldn't update any record and throw that exception.
I'm posting this information because I don't know a easy way to disable optimistic locking in the Ebean version that comes with Play <2.4.x and you probably will find that error again. In general you can minimize it using a version field and using transactions or reload/retry the operation.
I have an alternative solution to this problem
you can use:
#EntityConcurrencyMode(ConcurrencyMode.NONE)
in your Entity Class
This will disable the optimistic locking concurrent modification check
the new sql query will be:
update TABLE-NAME SET PARAM1=? WHERE ID = ?
the EntityConcurrencyMode comes in package
package com.avaje.ebean.annotation;
This is how I created the entities
Key reviewsKey = KeyFactory.createKey("Reviews", "Reviews");
Entity reviewEntity = new Entity("aReview", reviewsKey);
....
This is how I am trying to get an entity by its key
Key key = KeyFactory.createKey("Reviews", "Reviews");
Entity reviewEntity = datastore.get(key.getChild("aReview", reviewId));
....
I know there is an entity with key = 14 but i keep getting an exception that there is no entity found
Reviews("Reviews")/aReview("14")
What am I doing wrong?
The issue was it was looking for a string ID when it should have been Long
I have parsed it to Long and it works now.
Key key = KeyFactory.createKey("Reviews", "Reviews");
Entity reviewEntity = datastore.get(key.getChild("aReview", Long.parseLong(reviewId)));
I am using JPA to retrieve data from an Oracle XMLType column. I created a customizer:
#Override
public void customize(final ClassDescriptor descriptor) throws Exception {
descriptor.removeMappingForAttributeName("content");
DirectToXMLTypeMapping mapping = new DirectToXMLTypeMapping();
mapping.setAttributeName("content"); //name of the atribute on the Entity Bean
mapping.setFieldName("CONTENT"); //name of the data base column
mapping.getField().setColumnDefinition("XMLTYPE");
descriptor.addMapping(mapping);
}
and the column in my entity class is:
#Basic(optional = false)
#NotNull
#Lob
//#Column(name = "CONTENT", columnDefinition="XMLTYPE")
private String content;
However, when I run my program I get the error "java.lang.ClassCastException: oracle.xdb.XMLType cannot be cast to java.lang.String
at entities.Sqdocument._persistence_set(Sqdocument.java)
at org.eclipse.persistence.internal.descriptors.PersistenceObjectAttributeAccessor.setAttributeValueInObject(PersistenceObjectAttributeAccessor.java:46)
at org.eclipse.persistence.mappings.DatabaseMapping.setAttributeValueInObject(DatabaseMapping.java:1532)
at org.eclipse.persistence.mappings.DatabaseMapping.readFromRowIntoObject(DatabaseMapping.java:1423)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:448)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:803)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:607)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:564)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:777)
at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:462)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1150)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:852)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1109)
at org.eclipse.persistence.queries.ReadObjectQuery.execute(ReadObjectQuery.java:421)
at org.eclipse.persistence.internal.sessions.AbstractSession.internalExecuteQuery(AbstractSession.java:2946)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1602)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1584)
at org.eclipse.persistence.internal.indirection.NoIndirectionPolicy.valueFromQuery(NoIndirectionPolicy.java:323)
at org.eclipse.persistence.mappings.ForeignReferenceMapping.valueFromRowInternal(ForeignReferenceMapping.java:2135)
at org.eclipse.persistence.mappings.OneToOneMapping.valueFromRowInternal(OneToOneMapping.java:1716)
at org.eclipse.persistence.mappings.ForeignReferenceMapping.valueFromRow(ForeignReferenceMapping.java:2024)
at org.eclipse.persistence.mappings.ForeignReferenceMapping.readFromRowIntoObject(ForeignReferenceMapping.java:1369)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:448)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:803)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildWorkingCopyCloneNormally(ObjectBuilder.java:719)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObjectInUnitOfWork(ObjectBuilder.java:672)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:605)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:564)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:777)
at org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:783)
at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:434)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1150)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:852)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1109)
at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:393)
"
What could be the problem? Thanks.
Eclipselink is successfully retrieving the XML type. The problem is that XMLType is not an instance of String so it can't be automatically converted.
You need to write a Converter to convert between XMLType and String. You'll also need to write the other side of the converter which goes from String to XMLType, if you want to alter the data in any way.
Take a look at Conversions and Converters for help writing a converter.