Updating JPA entity fails on null value - mysql

I am using JPA (Hibernate) to store an entity on a MySQL 5.0 server.
Here is the entity in simplified form:
#Entity
#Table(name = "messages")
public class Message implements Serializable
{
#Id
#GeneratedValue
#Column
private long id;
#Column
private String content;
#Column(insertable = false)
private Date read;
#Column(insertable = false)
private Date deleted;
}
The columns "read" and "deleted" in table "messages" are defined so that they can contain NULL values. When I first tried to persist one of these entities I got an exception. Apparently, what Hibernate was doing there was listing the "read" and "deleted" columns in the column list of the insert statement but not in the value list. I got around that problem with the "insertable = false" statement in the #Column annotations you see above.
Now, however, I have a bigger problem. I want to set the read or date fields to non-null values. When I do that, I get a similar exception "You have an error in your SQL syntax". What he is doing now is listing all fields in the where part of the update statement, including "read" and "deleted". And what he does is check like "...and read=NULL". Which, in MySQL, of course should be "...and read IS NULL".
Rummaging around, I already found the "updatable" parameter for the #Column annotation. But if I set that to false, both "read" and "deleted" are never updated at all, so that is not what I am looking for, either.
...Help?

read is a reserved word in mysql and it looks like the driver is not escaping the name with back quotes. I think the best solution is to rename the column.

Related

Spring, Hibernate, Delete record older than 5 Min

I have a table called 'otp', I need to delete records that is older than a certain period, let say 5 minutes for now.
I use Spring Framework with hibernate with MySql. I tried all possible ways, writing a '#Query' in the DTO interface, trying the Spring Data query way, I even tried to do a 'Select' on the records. When running in the code it does not select any records nor does it delete any records older than the period asked for. When I copy the 'Query" statement into the MySQL workbench it do work, I only need to put the DB name in front of the table name to get it to work. Below is snippets of the code. I removed the "#Query" statement and kept the Spring Data query.
Thank you in advance for your help.
The Entity or model.
'
#Getter
#Setter
#NoArgsConstructor
#Entity
#Table(name = "otp")
public class Otp {
#Id
#Column(name="phoneNumber")
private String phoneNumber;
#Column(name="otp")
private String otp;
#Column(name="createdOn", nullable = false, updatable = false)
#CreationTimestamp
private Timestamp createdOn;
}
`
Code that must delete the rows.
'
#Repository
public interface OTPdto extends CrudRepository<Otp, String> {
#Modifying
#Transactional
void deleteByCreatedOnLessThan(Object now);
}
'
Code that build the date for the query.
'
Date now = Date.from(Instant.now());
Object param = new java.sql.Timestamp(now.getTime());
otPdto.deleteByCreatedOnLessThan(param);
'
Hard to tell by provided info, but maybe I can help.
First of all, I would make sure that CreatedOn column gets filled in with correct information. Also I would try something like deleteByCreatedOnBefore(java.sql.Timestamp time) instead of deleteByCreatedOnLessThan(Object now). java.sql.Timestamp is not raw timestamp value, so maybe comparasing "LessThan" doesn't work well with that.
I will go another route and just clear the otp field. Seems I need to keep the phone number in the Db. Will use the phone number in future to remove the record.

mysql : why would I require AttributeConverter over enum to map a DB column having enum datatype as enum with a JPA entity?

I am having a user database table as:
CREATE TABLE IF NOT EXISTS `user` (
`user_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`first_ name` VARCHAR(45) NOT NULL,
`active_status` ENUM('ACTIVE', 'PENDING', 'DEACTIVATED', 'BLOCKED', 'SPAM', 'DELETED') NOT NULL ,
UNIQUE INDEX `unique_id_UNIQUE` (`unique_id` ASC),
UNIQUE INDEX `email_UNIQUE` (`email` ASC),
PRIMARY KEY (`user_id`))
ENGINE = InnoDB;
I mapped it to a corresponding JPA entity class as:
#Entity
public class User implements OfloyEntity {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private int userId;
//other fields
#Enumerated(EnumType.STRING)
#Column(name = "active_status", nullable = false, length = 11)
private UserStatus activeStatus;
As you can see, I have mapped activeStatus to a enum UserStatus to restrict the entires from persistence layer itself.
public enum UserStatus {
ACTIVE,
PENDING,
DEACTIVATED,
BLOCKED,
DELETED,
SPAM
}
I want to know is there any drawback of using this approach for implementing a DB enum in persistence layer? I gone throw multiple articles which recommend using AttributeConverter but since the the values in my enum are very limited and have less chances of modification, I am unable to relate all those articles with my requirement.
Is there something I am missing, or any improvement can be done in my design?
Articles I gone throw:
vladmihalcea
thorban and some other stackoverflow questions.
Update: After reading the answer from Jens, I decided to implement AttributeConverter(for user's gender). And that confused me a little:
Why I decided to use enum as MYSQL column type : as it restrict the values and require less space. Because MYSQL stores the ordinal value of it's enum behind the scene and when asked for the value it represents the String value of that, it saves space.
My implementation of gender:
public enum UserGender {
MALE('M'),
FEMALE('F'),
OTHER('O');
private Character shortName;
private UserGender(Character shortName) {
this.shortName = shortName;
}
public Character getShortName() {
return shortName;
}
public static UserGender fromShortName(Character shortName) {
switch (shortName) {
case 'M': return UserGender.MALE;
case 'F' : return UserGender.FEMALE;
case 'O' : return UserGender.OTHER;
default:
throw new UserGenderNotSupportedException("user gender with shortName : " + shortName + " not supported");
}
}
}
converter class:
#Converter(autoApply = true)
public class UserGenderConverter implements AttributeConverter<UserGender, Character> {
#Override
public Character convertToDatabaseColumn(UserGender userGender) {
return userGender.getShortName();
}
#Override
public UserGender convertToEntityAttribute(Character dbGender) {
return UserGender.fromShortName(dbGender);
}
}
Now, the major doubts:
1. As per blogs, using MYSQL enum is evil in DB, because someday if I need to add extra values to the enum column and that would require a table ALTER, but isn't it the same case with using AttributeConverter? Because there also we use a java enum, which would need to be change if someday new genders are required?
2. If I use AttributeConverter, I would have to document java enum(UserGender here) explaination somewhere so that DBA can understand what F,M,O stands for. Am I right here?
The articles gave you a rich selection of potential drawbacks:
Using #Enumerated(EnumType.STRING) has the following:
It uses lots of space compared to other options. Note that means more data needs to be loaded and transferred over the wire this has an effect on performance as well. We have no idea if this is a problem for you and you won't know either until you made some performance tests.
Ties the name of the enum values hard to the column values. Which can be risky since developers are used to renaming stuff quickly and you would need tests with actual legacy data to catch this.
If you don't work with really huge amounts of data for which updating the column for all rows is an actual problem, I wouldn't sweat it. It's easy enough to introduce an AttributeConverter and update the data when the simple solution actually becomes a problem.
Update regarding the updated question:
I don't buy into the argument that anything is "evil" because it might require an ALTER TABLE statement. By this argument, we should abolish relational databases completely because using them requires DDL and evolution of an application will require more of it. Of course, the necessity of a DDL statement makes a deployment a little more complex. But you need to be able to handle this thing anyway.
But it is true that with an AttributeConverter you wouldn't need any DDL in this case, because you'd just put another value in the same column which doesn't have any special constraints except the maximum length of the value. This assumes you don't have a check constraint on the column to limit the legal values.
Do you have to document the relationship between Enum and the value stored in the DB? Depends on your team. Does the DBA even care about the meaning of the data? Does the DBA have access and the skills to understand the Java code? If the DBA needs or wants to know and can't or won't get the information from the source code you have to document it. True.

EclipseLink fails to fetch a scalar Boolean value

The following JPA criteria query succeeds on Hibernate (4.2.7 final).
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Boolean>criteriaQuery=criteriaBuilder.createQuery(Boolean.class);
Root<UserTable> root = criteriaQuery.from(entityManager.getMetamodel().entity(UserTable.class));
criteriaQuery.multiselect(root.get(UserTable_.enabled));
ParameterExpression<String>parameterExpression=criteriaBuilder.parameter(String.class);
criteriaQuery.where(criteriaBuilder.equal(criteriaBuilder.lower(criteriaBuilder.trim(root.get(UserTable_.emailId))), criteriaBuilder.lower(criteriaBuilder.trim(parameterExpression))));
List<Boolean> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, "admin").getResultList();
for(Boolean o:list)
{
System.out.println("enabled : "+o);
}
It is simply meant to return a scalar Boolean value from MySQL database. The corresponding column is of type TINYINT(1) in MySQL.
It generate the following SQL statement.
SELECT
usertable0_.enabled as col_0_0_
FROM
social_networking.user_table usertable0_
WHERE
lower(trim(BOTH FROM usertable0_.email_id))=lower(trim(BOTH FROM ?))
The same query fails on EclipseLink (2.5.1) in which case it returns nothing (no error, no exception). It however, generates a correct SQL statement as follows.
SELECT enabled
FROM projectdb.user_table
WHERE (LOWER(TRIM(email_id)) = LOWER(TRIM(?)))
bind => [admin]
The corresponding JPQL like so,
SELECT u.enabled
FROM UserTable u
WHERE lower(trim(u.emailId))=lower(trim(:emailId))
also doesn't get the value in question.
It however, works with additional columns as shown below.
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]>criteriaQuery=criteriaBuilder.createQuery(Object[].class);
Root<UserTable> root = criteriaQuery.from(entityManager.getMetamodel().entity(UserTable.class));
criteriaQuery.multiselect(root.get(UserTable_.enabled), root.get(UserTable_.firstName));
ParameterExpression<String>parameterExpression=criteriaBuilder.parameter(String.class);
criteriaQuery.where(criteriaBuilder.equal(criteriaBuilder.lower(criteriaBuilder.trim(root.get(UserTable_.emailId))), criteriaBuilder.lower(criteriaBuilder.trim(parameterExpression))));
List<Object[]> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, userName).getResultList();
One extra column root.get(UserTable_.firstName) is added and the return type of CriteriaQuery is changed from CriteriaQuery<Boolean> to CriteriaQuery<Object[]>.
The column is defined as follows in the corresponding entity.
#Basic(optional = false)
#NotNull
#Column(name = "enabled")
private Boolean enabled; //Getter and setter.
Why doesn't it given a Boolean value in EclipseLink?
This is due to EclipseLink bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=340089 which causes EclipseLink to be unable to distinguish the value returned from the SQL query from the construct it uses internally to indicate there were no results. Selecting another value is the only workaround, but it seems simple enough that not many people hit it or just workaround it without commenting or voting for the bug.

Why is persist in JPA clearing the existing data in the table row?

I am trying to update data to a mySQL database using JPA. I have no problem persisting data but flush will not work as expected. I retrieve the id for the login session, set that id (it is the primary key) along with setting the description field that I want merged to the database. I have debugged line by line through this method and all variables contain the expected values. Any ideas or suggestions to overcome this problem are appreciated.
public String update() {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
if(true){
em.getTransaction().begin();
String sessionEmail=Util.getEmail();
//Create query to find user passwords matching the inputted name
Query myQuery = em.createQuery("SELECT u FROM BusinessAccount u WHERE u.email=:email");
myQuery.setParameter("email", sessionEmail);
List<BusinessAccount> accounts=myQuery.getResultList();
int intId=accounts.get(0).getId();
businessAccount.setId(intId);
String des=businessAccount.getDescription();
businessAccount.setDescription(des);
em.flush();
addMessage(new FacesMessage(FacesMessage.SEVERITY_INFO,
"User Registration Successful!", null));
return "success";
}
else {
addMessage(new FacesMessage(FacesMessage.SEVERITY_ERROR,
"User Registration Failed!", null));
return "failure";
}
}
merge() persists all the state of the entity. Not just the non-null fields. I it wasn't, you would complain that you want to set some field to null and that merge() ignores it and leaves it as is.
So get an entity from the database, and modify it, instead of only gettings its ID, creating a new entity instance from scratch and only settings some of its fields.
Note that, if you get the entity and modify it inside a single transaction, you don't even have to call merge(): the new state will be made persistent automatically.

Getting the ID (PK) of a newly persisted entity

I'm developing a J2EE 6 Web Application, using a MySql 5.02 DataBase. I'm trying to generate a hash digest of the ID, every time I create a new Entity. This is set to a column on the Table.
But well, I'm stuck on something that is apparently easy, and according with what I found googling, possible. Basically I want to retrieve the ID (Primary Key) of a newly persisted object, but whatever I try, it returns null.
The steps are follow are:
Create the Instance of the Entity -> userCard = new Users();
Setting the corresponding fields with some values.
Calling the persist() method of the EntityManager.
After reading some forums, I understood I had to either call flush() after persist(), or use merge() and use the returned entity to retrieve the id.
public void createAndFlush(Users users) {
em.persist(users);
em.flush();
}
public Integer edit(Users users) {
return ((Users)em.merge(users)).getIdvcards();
}
None of them (among some other combinations) work, well, the Entity is successfully persisted, but the ID field returns null.
These are the correspinding annotations of the id column I want to retrieve:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idvcards")
private Integer idvcards;
And obviously, the column idvcards of my table is set to Auto-Increment and Primary Key.
Maybe I just need to clarify my Peristence-Management concepts, but I'd appreciate if I can get some hint to solve this basic issue.
Maybe is not the most elegant solution ever, but finally I succeded on retrieving the ID of the new Entity:
public Integer create(User user) {
em.persist(users);
em.flush();
return (Integer) em.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(users);
}
And well, althought is not related to the functionality, I changed the entity name to the singular form as #Bohemian suggested.
Try this, it works for me:
#Id
#GeneratedValue
#Column(name = "id", unique = true, nullable = false)
Also, I don't use the value returned from merge. I just persist then flush and the entity object magically gets the new key value.
p.s. Tables should never be named in the plural, and especially not the entity class. Call the class User (and the table if you can). Otherwise it's just confusing: Users sounds like a collection of User.
It will work fine
em.persist(usmApproveTransaction);
em.flush();
System.out.println("++++++e++++++++"+usmApproveTransaction.getUatApproveTransIdPk());
return usmApproveTransaction;
I'm getting id using em.flush(); after persist
++++++e++++++++51472