How to use to converter in sping jpa - java

To run the application i use tomcat 8.5.50 package in war.
i use spring 5.2 version.
in my code i want to use LocalDataTime like this:
#Entity
#Table(name="meals")
public class Meal {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#Column(name = "date_time")
#Convert(converter = MealConverter.class)
private LocalDateTime datetime;
#Column(name = "description")
private String description;
#Column(name = "calories")
private int calories;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public LocalDateTime getDatetime() {
return datetime;
}
public void setDatetime(LocalDateTime datetime) {
this.datetime = datetime;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getCalories() {
return calories;
}
public void setCalories(int calories) {
this.calories = calories;
}
}
my Converter:
#Converter(autoApply = true)
public class MealConverter implements AttributeConverter<Meal, String> {
private static final String SEPARATOR = ", ";
#Override
public String convertToDatabaseColumn(Meal meal) {
StringBuilder sb = new StringBuilder();
sb.append(meal.getCalories()).append(SEPARATOR)
.append(meal.getDatetime())
.append(SEPARATOR)
.append(meal.getDescription());
return sb.toString();
}
#Override
public Meal convertToEntityAttribute(String dbData) {
String[] rgb = dbData.split(SEPARATOR);
Meal meal = new Meal(Integer.valueOf(rgb[0]),
LocalDateTime(valueOf(rgb[1]),
rgb[2],
rgb[3]);
return meal;
}
}
I am trying to use the converter in the convertToEntityAttribute method but the compiler does not allow me to do this. What needs to be fixed in my Converter?

Meal meal = new Meal(Integer.valueOf(rgb[0]),
LocalDateTime(valueOf(rgb[1]),
rgb[2],
rgb[3]);
Your Meal class doesn’t seem to have any explicit constructor, so you cannot pass arguments to new Meal(). You seem to be trying to pass two arguments. You may want to create a suitable constructor, or you may want to pass the two values into the Meal using setters after the object has been created.
LocalDateTime is a class, but you seem to try to call it as a method with three arguments. If that’s java.time.LocalDateTime, you probably intended LocalDateTime.of(someArguemts), but there isn’t any three-argument of method of that class. If you explain better what result you expect, we can guide you better.
As the first argument to LocalDateTime you have a call to a valueOf method that doesn’t seem to be declared in your class. You may have intended Integer.valueOf as in the preceding line.
If you are trying to use your RGB values for initializing a date (don’t know what sense that might make), be aware that if your RGB values go up to 255, this will likely fail with an exception since month numbers only go up to 12 and day of month up to 31.
I am far from sure that the following is correct or does what you want it to do, but it’s a guess at what you may be after.
#Override
public Meal convertToEntityAttribute(String dbData) {
String[] fields = dbData.split(SEPARATOR);
Meal meal = new Meal();
meal.setCalories(Integer.parseInt(fields[0]));
meal.setDateTime(LocalDateTime.parse(fields[1]));
meal.setDescription(fields[2]);
return meal;
}
I am trying to do the opposite of your convertToDatabaseColumn method. I have discarded the variable name rgb because I didn’t see how it couldn’t be misleading here.

Related

JOOQ + JPA entities

I'm new with JOOQ library and have one thing interesting me so much. I've implemented CRUD service on JOOQ at first and after that I've tried to avoid some duplicate code. For reach that goal I've added JPA repository and also added#Entity annotation to my generated by JOOQ class. And now I still want to use JOOQ for some cases (querying List using filter and sorting and pagination). But something went wrong and now after JOOQ makes a select request I can see nulls in my class's attributes.
I'm getting right count of entities by filter, but class's properties are null after mapping. Is that mapping wrong or I just could't use JOOQ and JPA together for this case?
My abstact class for all entities (as I said, for avoid duplicating code I've refactored some code and now use generics):
#MappedSuperclass
public abstract class AbstractServiceEntity {
private Integer id;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
My JPA class (generated by JOOQ):
/**
* This class is generated by jOOQ.
*/
#SuppressWarnings({ "all", "unchecked", "rawtypes" })
#Entity
#Table(schema = "ref", name = "account")
public class Account extends AbstractServiceEntity implements Serializable {
private static final long serialVersionUID = -162537472;
private Integer id;
private Integer transitId;
private Integer partnerId;
private String currencyCode;
private String descr;
private Long inCredit;
private Long balanceLimit;
private Long outCredit;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Integer transitPartnerId;
public Account() {}
public Account(Account value) {
this.id = value.id;
this.transitId = value.transitId;
this.partnerId = value.partnerId;
this.currencyCode = value.currencyCode;
this.descr = value.descr;
this.inCredit = value.inCredit;
this.balanceLimit = value.balanceLimit;
this.outCredit = value.outCredit;
this.createdAt = value.createdAt;
this.updatedAt = value.updatedAt;
this.transitPartnerId = value.transitPartnerId;
}
public Account(
Integer id,
Integer transitId,
Integer partnerId,
String currencyCode,
String descr,
Long inCredit,
Long balanceLimit,
Long outCredit,
LocalDateTime createdAt,
LocalDateTime updatedAt,
Integer transitPartnerId
) {
this.id = id;
this.transitId = transitId;
this.partnerId = partnerId;
this.currencyCode = currencyCode;
this.descr = descr;
this.inCredit = inCredit;
this.balanceLimit = balanceLimit;
this.outCredit = outCredit;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.transitPartnerId = transitPartnerId;
}
And my method extracting entities from DB:
#Repository
#RequiredArgsConstructor
public class JooqAccountRepository {
private final DSLContext jooq;
public List<Account> findAll(Condition filterCondition, SortField[] sortFields, Integer partnerId, Integer limit, Integer offset) {
return jooq.selectFrom(ACCOUNT)
.where(ACCOUNT.PARTNER_ID.equal(partnerId))
.and(filterCondition)
.orderBy(sortFields)
.limit(limit)
.offset(offset)
.fetchInto(Account.class);
}
public Integer findAccountsCount(Integer partnerId) {
return jooq.selectCount().from(ACCOUNT)
.where(ACCOUNT.PARTNER_ID.equal(partnerId))
.fetchOne(0, Integer.class);
}
}
As a result of my searches - I've made a mistake with annotations in Account class. If you want use these frameworks together, you should use #Column on entity's properties or setting your jooq's codegen plugin in different way)
This resource was usefull for me

How can I sort List<Object> CRUD Repository

I'm trying to build simple REST for purchases I need 2 methods. The first method should show all purchases sorted by date. The second one removes all purchases for specified date I made a method to add and to get all purchases. Now I'm stuck.
#Entity
#Table (name="purchase")
public class Purchase {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#CreationTimestamp
#Temporal(TemporalType.DATE)
#Column(name="createat")
private Date created;
#Column(name="price")
private BigDecimal price;
#Column(name="currency")
private String currency;
#Repository
public interface PurchaseRepository extends JpaRepository<Purchase, Long>
{
}
#Service
public class PurchaseService {
#Autowired
private PurchaseRepository purchaseRepository;
public void addPurchase(Purchase purchase) {
purchaseRepository.save(purchase);
}
public List<Purchase> getAllPurchase() {
List<Purchase> purchase = new ArrayList<>();
purchaseRepository.findAll().forEach(purchase::add);
return purchase;
}
}
#RestController
public class PurchaseController {
#Autowired
private PurchaseService purchaseService;
#PostMapping("/purchase")
public void addPurchase(#RequestBody Purchase purchase) {
purchaseService.addPurchase(purchase);
}
#RequestMapping("/purchase")
public List<Purchase> getAllTopics() {
return purchaseService.getAllPurchase();
}
}
What I need:
1. method to sort my List sorted by date
2. method that removes all purchases for specified date
You can use Spring Data JPA features in these cases.
Add the following methods to PurchaseRepository:
List<Purchase> findAllByOrderByCreatedAsc();
long deleteByCreated(Date created);
And after all, Spring is going to generate an appropriate query based on a method name.
I got it
long deleteByCreated(Date date);
#Transactional
public long deleteAllByDate(Date date){
return purchaseRepository.deleteByCreated(date);
}
#RequestMapping(method=RequestMethod.DELETE, value="/purchasess/{date}")
public long findAllByCreatedBetween(#DateTimeFormat(pattern="yyyy-MM-dd")
#PathVariable Date date){
return purchaseService.deleteAllByDate(date);
}

Extending/modifying generated entities in Hibernate

I am creating a REST api service for a mysql database. I've generated classes using IntelliJ's persistence tool. It does a pretty good job.
There are some quirks to the schema that I am working with. The users want the endpoints to be accessible by another property other than the "id" primary key column.
Ex: /object/<name property>' versus/object/`.
Here is the catch though. The schema can change. The name property is not going anywhere though so I can safely assume that will always be on the object.
I've learned that you can use Superclasses to force these generated entites to have custom properties without affecting the database schema. I dont want to make a model change in the generated entity and have that update the database table layout as it is not my database.
I have a class called Animal.
#Entity
#Table(name = "animals", schema = "xyz123", catalog = "")
public class AnimalEntity extends AnimalSuperclass {
private Integer id;
private String name;
private String description;
#Id
#Column(name = "id", nullable = false)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = true, length = 80)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "description", nullable = true, length = 255)
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RoleEntity that = (RoleEntity) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name) &&
Objects.equals(description, that.description);
}
#Override
public int hashCode() {
return Objects.hash(id, name, description);
}
}
I have to manually add extends AnimalSuperclass. Which is fine for now. Eventually I am going to try to generate these using .xmls on runtime.
Then I have this superclass..
#MappedSuperclass
public class AnimalSuperclass implements Serializable {
private String testMessage;
private String name;
private Integer id;
#Transient
public String getTestMessage() {
return this.testMessage;
}
public void setTestMessage(String id) {
this.testMessage = testMessage;
}
}
What I want to do is force the #Id annotation to be on the name property from within the superclass. Something like this..
#MappedSuperclass
public class AnimalSuperclass implements Serializable {
private String testMessage;
private String name;
private Integer id;
#Transient
public String getTestMessage() {
return this.testMessage;
}
public void setTestMessage(String id) {
this.testMessage = testMessage;
}
#Basic
#Id
#Column(name = "name", nullable = false, length = 15)
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
#NaturalId
#Column(name = "id", nullable = false)
private Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
}
How do I go about doing that? Currently this throws an error when I hit the endpoint: {"cause":null,"message":"Id must be assignable to Serializable!: null"}
Java is not my first language so I am not an expert by any means. But from what I've read, its not possible to override subclass properties from the superclass. Is there a better way to approach this, maybe by using RepositoryRestConfiguration? I am using PagingAndSortingRepository to serve these entities. I cannot extend the entities and use my superclass as a child as that creates a dType property in the schema and I cannot alter the table layout.
There is no hard link between the request and your entity. In your repository you can write methods that can query the data that is brought it from the request.
For example if they are requesting a name you can do something like
Page<AnimalEntity> findByName(String name, Pageable pageable);
in your Repository. Spring will take care of the rest and then you can call this in your controller.
#Service
public class AnimalService {
#Autowired
private AnimalEntityRepository animalRepo;
public Page<AnimalEntity> findAnimal(String name) {
Page<AnimalEntity> animals = animalRepo.findByName(name, new PageRequest(1,20));
return animals;
}
}
One thing to mention is that depending on how you configured Hibernate when sending an entity back to the client and the entity is seralized you might get an failed to lazy initialize error. If that is the case your entities will have to be converted to a POJO (plain old java object) and that sent back.

How Do I Create this Nested Object?

So my front end colleague has successfully made this cool calendar using angular.uicalendar, that I'm trying to accommodate on the back end using java persistence and Spring MVC. The Calendar object itself has a lot of information and he's passing it as a nested object.
The parent object seems to have a “Start” and “End” which are LocalDateTimes, and a title which is a string. Then within that, there is an Email object (boolean sendEmail, int time (as in how long until you want it to be sent)) Then on that same layer there’s a text object which contains two variables that do virtually the same thing, but for texts, and also another called notification, which also does the same, but whether to post it on the website or not. Here's a screenshot of how it looks on the front end side if that helps.
Screenshot of Calendar Object
So I'm trying to replicate the object on my end so that I can store/edit the objects and I'm confused about how to proceed. My independent research suggests I can use an "#Embedded" annotation? One intriguing suggestion I had was "making the parameter into a Hashmap." Apparently Spring should be able to parse it into that if I don't have a special class prepared to store it. But I'm sure what that would look like. Could I even store the object in the database with this HashMap hanging off the end of it? Currently my calendarEvent class looks like so, and it's my attempt to make sense of what this Object would look like:
package com.theironyard.Entities;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* Created by macbookair on 12/16/15.
*/
#Entity
#Table (name = "events")
public class CalendarEvent {
#Id
#GeneratedValue
public int id;
#Column(nullable = false)
public LocalDateTime start;
#Column(nullable = false)
public LocalDateTime end;
#Column(nullable = false)
public String title;
//possibly use #Embedded here ?
public Object email() {
boolean send;
int time; // hours before sending email
return email();
}
public Object text() {
boolean send;
int time;
return text();
}
public Object notification() {
boolean send;
int time;
return notification();
}
public CalendarEvent(int id, LocalDateTime start, LocalDateTime end, String title) {
this.id = id;
this.start = start;
this.end = end;
this.title = title;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public LocalDateTime getStart() {
return start;
}
public void setStart(LocalDateTime start) {
this.start = start;
}
public LocalDateTime getEnd() {
return end;
}
public void setEnd(LocalDateTime end) {
this.end = end;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
So I guess in summary: What's the best way to store this object? Can I store the main object in the database with a HashMap hanging off of it?
Thanks everybody.

NullPointerException thrown on flush after upgrading to Hibernate 4.1.3 Final

I have just completed an upgrade from Hibernate 3.6 to 4.1.3 Final and at first everything seemed to go fine. However, one of my colleagues recently tested this an in one scenario he gets a NullPointer being thrown from within Hibernate (and this exception was not being thrown before we upgraded for the exact same DB). It is an incredibly strange scenario. We have an entity called BlogPost that looks like the below and it extends some mapped superclasses (that I have also included):
#Entity
#Table(name = "blog_post")
public class BlogPost extends CommunityModelObject implements HasFeedPost {
#Lob
private String title;
#Lob
private String content;
#Enumerated
#Column(nullable = false)
private CBlogPost.Status status = CBlogPost.Status.UNPUBLISHED;
// Reference to the feed post that indicates that this blog post has been published
#OneToOne
#JoinColumn(name = "feed_post_id")
private FeedPost feedPost;
#ManyToOne
#JoinColumn(name = "posted_by_employee_id")
private Employee postedBy;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public CBlogPost.Status getStatus() {
return status;
}
public void setStatus(CBlogPost.Status status) {
this.status = status;
}
#Override
public FeedPost getFeedPost() {
return feedPost;
}
#Override
public void setFeedPost(FeedPost feedPost) {
this.feedPost = feedPost;
}
public Employee getPostedBy() {
return postedBy;
}
public void setPostedBy(Employee postedBy) {
this.postedBy = postedBy;
}
}
#Filter(name = "tenantFilter", condition = "(tenant_id = :tenantId or tenant_id is null)")
#MappedSuperclass
public abstract class CommunityModelObject extends ModelObject {
#IndexedEmbedded(prefix = "tenant", indexNullAs = IndexedEmbedded.DEFAULT_NULL_TOKEN)
#ManyToOne
#JoinColumn(name = "tenant_id")
protected Tenant tenant;
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
/**
* If the Tenant is null then it can be accessed / viewed by the entire "community" / user base
*/
public boolean isCommunityObject() {
return tenant == null;
}
}
#MappedSuperclass
public abstract class ModelObject extends BaseModelObject {
#Id
#GeneratedValue
private Long id;
#Override
public long getId() {
return (id == null ? 0 : id);
}
public void setId(long id) {
this.id = (id == 0 ? null : id);
}
}
#MappedSuperclass
public abstract class BaseModelObject implements java.io.Serializable {
// This annotation ensures that a column is not associated with this member (simply omitting the #Column annotation is not enough since
// that annotation is completely optional)
#Transient
private boolean doNotAutoUpdateDateUpdated = false;
#Version
protected int version;
#Column(name = "date_created")
protected Date dateCreated;
#Column(name = "date_updated")
protected Date dateUpdated;
public abstract long getId();
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public Date getDateCreated() {
return dateCreated;
}
public Date getDateUpdated() {
return dateUpdated;
}
/**
* This will set the dateUpdated to whatever is passed through and it will cause the auto update (pre-update) to NOT occur
*
* #param dateUpdated
*/
public void setDateUpdated(Date dateUpdated) {
doNotAutoUpdateDateUpdated = true;
this.dateUpdated = dateUpdated;
}
public void touch() {
// By setting date updated to null this triggers an update which results in onUpdate being called and the nett
// result is dateUpdated = new Date()
dateUpdated = null;
}
#PrePersist
protected void onCreate() {
dateCreated = new Date();
}
#PreUpdate
protected void onUpdate() {
if (!doNotAutoUpdateDateUpdated) {
dateUpdated = new Date();
}
}
#Override
public boolean equals(Object obj) {
long id = getId();
if (id == 0) {
return this == obj;
}
//Use Hibernate.getClass() because objects might be proxies
return obj != null &&
obj instanceof BaseModelObject &&
Hibernate.getClass(this) == Hibernate.getClass(obj) &&
getId() == ((BaseModelObject)obj).getId();
}
#Override
public int hashCode() {
Long id = getId();
return id == 0 ? super.hashCode() : id.intValue();
}
#Override
public String toString() {
return getClass().getSimpleName() + "-" + getId();
}
}
The strangest thing is happening when I query BlogPost in some scenarios. If I run the query below, for example, in isolation then it works fine but if I run it in amongst a bunch of other queries then I get the exception below:
select b from BlogPost b
java.lang.NullPointerException
at org.hibernate.event.internal.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:240)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:163)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:225)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:55)
at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1153)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1208)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:256)
Now the kicker is that if I take all of the fields from all of the mapped superclasses that I listed above and put them directly into BlogPost and make BlogPost just extend nothing and implement java.io.Serializable then everything works perfectly. This leads me to believe that the bug is either related to mapped superclasses or the Hibernate filter that I am applying to CommunityModelObject.
Any ideas as to how to solve this? I am assuming that it is a newly introduced bug in Hibernate but I may be wrong. This is causing major issues for us since we need to upgrade asap in order to upgrade Hibernate Search which we need to do for a critical bug fix.
Also note that the DB we are using is MySQL with the following custom dialect that I wrote when doing this upgrade to handle our BIT columns:
public class MySQL5InnoDBDialectExt extends MySQL5InnoDBDialect {
private static final String BIT_STRING = "bit";
public MySQL5InnoDBDialectExt() {
super();
registerColumnType(Types.BOOLEAN, BIT_STRING);
}
}
Thanks,
Brent
I sorted this issue out, found the problem by fluke. Here is the resolution as I posted it on the Hibernate forum:
I found the issue. It does not seem to be related to interceptors,
rather to either caching or instrumentation. Basically our app
automatically includes all entities within a very specific package in
our caching scheme and the same classes in our instrumentation. We
generally have all of our entities in this package, however this one
which was causing the issue was the only one not included in this
package. The previous version of EhCache / Hibernate that we were
using seemed ok with this, but after upgrading it caused issues.
Anyway, the entity was in the incorrect package, when I refactored it
and moved it into the correct package then everything worked! So it
was not a bug in Hibernate, just an informative exception that made it
difficult to track this issue down (I basically solved it by complete
fluke).
Hope this helps somebody, but in my case it was a problem with a wrong instrumentation.
I have class 'A' and two child classes 'B' and 'C'. 'A' class has a lazy property and it is instrumented to make the lazy accessing works.
But the mistake was that I didn't instrument the child classes 'B' and 'C', therefore any access to the instrumented property from 'B' and 'C' caused the exception.
When I instrumented 'B' and 'C', the problem went away.

Categories

Resources