Using JPA 2.1 Converter with Java 1.8 Time API reults in Eclipse JPA-Error "The left and right expressions type must be of the same type" - eclipse

I'm using Eclipe IDE Neon.2 working on a JavaEE 7 project (JPA 2.1 & Java 8).
In my Entities new Java 8 Time API is used and therefore i need to use JPA-AttributeConverter to convert them to sql-datatypes. In most cases i want to use named queries with JPQL.
#Entity
#NamedQuery(name = Account.QUERY_FIND_LOGGED_USER_TODAY, query = "SELECT a from Account a where a.lastLoggedInDate == CURRENT_DATE")
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
public final static String QUERY_FIND_LOGGED_USER_TODAY = "account_find_logged_user_today";
#Id
private Integer id;
private String name;
#Convert(converter = LocalDateAttributeConverter.class)
private LocalDate lastLoggedInDate;
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getLastLoggedInDate() {
return this.lastLoggedInDate;
}
public void setLastLoggedInDate(LocalDate lastLoggedInDate) {
this.lastLoggedInDate = lastLoggedInDate;
}
}
The AttributeConverter looks like this
#Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> {
#Override
public Date convertToDatabaseColumn(LocalDate locDate) {
return (locDate == null ? null : Date.valueOf(locDate));
}
#Override
public LocalDate convertToEntityAttribute(Date sqlDate) {
return (sqlDate == null ? null : sqlDate.toLocalDate());
}
}
Eclipse shows me an error-message pointing to "a.lastLoggedInDate == CURRENT_DATE" saying
The left and right expressions type must be of the same type.
Is this a real JPA-Error, or does Eclipse-JPA-Validation not support the combination JPA-Converter & JPQL.

Related

POST REST request including a foreign key OnToMany Mapping

i'm new to Springboot. I'm trying to implement a simple REST api using :
-Springboot, JPA & rest along with hibernate
I have a 2 tables database, Notebook that contains 1 to many notes
I already setup the 2 tables and relationships. I also created a NotebookRepository and NoteRepository to get basic CRUD operations via the springboot rest. The Database connection and relationships are functionning
but i don't know how to add a new note (it has a notebook_id foreign key which msut NOT be NULL) and everytime i tryto post something along these lines
{
"title:"abc",
"text":"whatever",
"notebook":{
"id":2
}
}
i get this error :
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'notebook_id' cannot be null
#Entity
#Table(name="notebook")
public class NoteBook {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#OneToMany(mappedBy="notebook", cascade=CascadeType.ALL)
List<Note> notes;
public NoteBook() {
}
public NoteBook(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Note> getNotes() {
return notes;
}
public void setNotes(List<Note> notes) {
this.notes = notes;
}
public void addNote(Note note) {
if(notes == null) {
notes = new ArrayList<>();
}
note.setNotebook(this);
notes.add(note);
}
#Entity
#Table(name="note")
public class Note {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="title")
private String title;
#Column(name="text")
private String text;
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.DETACH, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="notebook_id")
private NoteBook notebook;
public Note() {
}
public Note(String title, String text) {
this.title = title;
this.text = text;
}
#RepositoryRestResource(collectionResourceRel = "note", path = "notes")
public interface NoteRepository extends JpaRepository<Note, Integer>{
//No code...
}
#RepositoryRestResource(collectionResourceRel = "notebook", path = "notebooks")
public interface NotebookRepository extends JpaRepository<NoteBook, Integer>{
}
The problem is that the class Note doesn't have a constructor with NoteBook parameter to pass the created NoteBook object to, so the solution is to add this constructor:
public Note(String title, String text, NoteBook noteBook) {
this.title = title;
this.text = text;
this.noteBook = noteBook;
}
and it's enough to send the JSON object as you do, but just be aware of case-sensitivity:
{ "title:"abc", "text":"whatever", "noteBook":{ "id":2 } }
I think you need to add referencedColumnName = "id" for JoinColumn annotation for notebook field in Note class.
Maybe you have problem with IDENTITY generation type. See this problem with null pointer

Creating JPA entity with composite primary key with #Id from #MappedSuperclass

I have a class hierarchy for JPA entities with the base class being a MappedSuperclass which has one ID defined. I am trying to use a composite key in a subclass however that does not seem to work
My code looks like this
#MappedSuperclass
public class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
#Entity
#EntityListeners(EntityBaseListener.class)
#Inheritance(strategy=InheritanceType.JOINED)
#Table(name = "catalog_entity")
public class BaseCatalogEntity extends BaseEntity {
#Column(name = "created_at", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
#Column(name = "updated_at", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date updatedAt;
public void setCreatedAt(Date date)
{
createdAt = date;
}
public void setUpdatedAt(Date date)
{
updatedAt = date;
}
public Date getCreatedAt() {
return createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
}
#Entity
#Table(schema = "student_catalog")
#IdClass(value = StudentCatalog.StudentCatalogPK.class)
public class StudentCatalog extends BaseCatalogEntity {
#Id
#Column(name = "name", nullable = false, length = 100)
private String name;
#Id
#Column(name = "version", nullable = false)
private Integer version;
#Column(name = "description" , length = 255)
private String description;
#Column(name = "vendor" , length = 50)
private String vendor;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVendor() {
return vendor;
}
public void setVendor(String vendor) {
this.vendor = vendor;
}
public static class StudentCatalogPK implements Serializable {
private Long id;
private String name;
private Integer version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
#Override
public boolean equals(Object obj) {
boolean result = false;
if(obj != null && (obj instanceof StudentCatalogPK)) {
StudentCatalogPK other = (StudentCatalogPK)obj;
result = (Objects.equals(this.id, other.id) && Objects.equals(this.name, other.name) &&
Objects.equals(this.version, other.version));
}
return result;
}
#Override
public int hashCode() {
return (27780 + (this.id != null ? this.id.hashCode() : 0) +
(this.version != null ? this.version.hashCode() : 0) +
(this.name != null ? this.name.hashCode() : 0));
}
}
}
I get the following exception:
Exception Description: Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [com.example.jpa.StudentCatalog$StudentCatalogPK] and those of the entity bean class [class com.example.jpa.StudentCatalog] must correspond and their types must be the same. Also, ensure that you have specified ID elements for the corresponding attributes in XML and/or an #Id on the corresponding fields or properties of the entity class.
I am using Eclipselink 2.5.1. Is there a way I can get this to work without changing the BaseEntity and BaseCatalogEntity classes?
It is not legal in JPA to redefine the id in subclasses. This would lead to ambiguities in the table mappings as well as in polymorphic queries.
The desire to extend the key defined in a superclass is a common issue when business keys are used for DB identity. I would advise to use only surrogate keys (like UUID) for DB identity and business keys for instance identity.
Under following conditions:
your base entity should use TABLE_PER_CLASS inheritance (and as I can see it is)
your base entity (composite key) key is of the same type as that one you want to have in your derived class (so there should be also composite key of String and Integer).
You can use #AttributeOverride annotation under class declaration, removing #Id fields from it:
#AttributeOverride(name = "id", column = #Column(name = "NAME"))
This - in result, can change column name in derived entity's table and that's the most you can acheive.
When using #MappedSuperClass, it would be advisable to make the BaseEntity Class as abstract and then extending the Base class from other Entity classes.
Cleaner approach keeping inheritance in mind and designing your application.

JPA One-to-Many relationship using a List - OrderBy ignored/not working

I'll try to formulate the question more simple:
#Entity
public class One implements Serializable {
...
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("name ASC")
private List<Many> many;
...
First I populate the List with some Many-Entities and persist the One-Entity. Second I retrieve (em.find) the One-Entity expecting the List in ascending order by Many#name, but it's not ordered by name. The List is ordered by id. Complete code see below if necessary.
Original post some days ago:
I'm using a current Netbeans Glassfish bundle.
Product Version: NetBeans IDE 8.0 (Build 201403101706)
Updates: NetBeans IDE is updated to version NetBeans 8.0 Patch 2
Java: 1.7.0_51; Java HotSpot(TM) 64-Bit Server VM 24.51-b03
Runtime: Java(TM) SE Runtime Environment 1.7.0_51-b13
System: Mac OS X version 10.9.3 running on x86_64; UTF-8; de_DE (nb)
The JPA #OrderBy annotation is completely ignored.
#Entity
public class One implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#OneToMany
#OrderBy("name ASC")
private List<Many> many;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Many> getMany() {
return many;
}
public void setMany(List<Many> many) {
this.many = many;
}
}
The many Entity
#Entity
public class Many implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
private String name;
public Many() {
}
public Many(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The service class (EJB)
#Stateless
public class Service {
#PersistenceContext(unitName = "cwPU")
private EntityManager em;
public One createOne() {
return em.merge(new One());
}
public Many createMany(String name) {
return em.merge(new Many(name));
}
public One add(Long oneId, Long manyId) {
One one = em.find(One.class, oneId);
Many many = em.find(Many.class, manyId);
one.getMany().add(many);
return one;
}
public One find(Long id) {
One one = em.find(One.class, id);
return one;
}
}
The main class
public class Main {
public static void main(String[] args) throws NamingException {
EJBContainer container = EJBContainer.createEJBContainer();
Context ctx = container.getContext();
Service service = (Service) ctx.lookup("java:global/classes/Service");
One one = service.createOne();
Many many = service.createMany("the-first");
service.add(one.getId(), many.getId());
many = service.createMany("a-second");
one = service.add(one.getId(), many.getId());
one = service.find(one.getId());
System.out.println("-------------------------------------------------");
for (Many m : one.getMany()) {
System.out.println(m.getName());
}
container.close();
}
}
The output:
the-first
a-second
No matter what I write to the #OrderBy annotation (name ASC, name DESC, id ASC, id DESC), the output is always the same ascending order by the id.
Any idea what I'm missing?
The #Orderby annotation doesn't actually work that way. According to the javadoc, the annotation "Specifies the ordering of the elements of a collection ...at the point when the collection is retrieved."
So the annotation affects the result of the query (find), but does not dictate the order in the collection you store the result set into.
The solution is calling em.refresh (at the right place) as stated from Chris and WPrecht. I had to do this in a separate EJB method.
This did not work:
public One find(Long id) {
em.refresh(em.find(One.class, id)); // did not work
One one = em.find(One.class, id);
return one;
}
Adding a separate refresh method
public void refresh(Long id) {
em.refresh(em.find(One.class, id));
}
and calling it in the main program
...
service.refresh(one.getId());
one = service.find(one.getId());
...
works!
Probably I have to do more reading to understand caching.

get max value of column using jpa criteria query

I want to get the maximum value of column relationId from table ElementRelationType
I have written code but its giving error
CriteriaBuilder cb1 = entityManager.getCriteriaBuilder();
CriteriaQuery<ElementRelationTypes> cq1 = cb1.createQuery(ElementRelationTypes.class);
Root<ElementRelationTypes> root = cq1.from(ElementRelationTypes.class);
cq1.select(cb1.max(root.get("relationId")));
select and max both giving error
how to get the integer max value
public class ElementRelationTypes {
private RelationId relationLangPK=new RelationId();
private Country country;
private Status status;
#EmbeddedId
public RelationId getRelationLangPK() {
return relationLangPK;
}
public void setRelationLangPK(RelationId relationLangPK) {
this.relationLangPK = relationLangPK;
}
#Transient
public Integer getRelationId() {
return getRelationLangPK().getRelationId();
}
public void setRelationId(Integer relationId) {
getRelationLangPK().setRelationId(relationId);
}
#Transient
public Language getLanguage() {
return getRelationLangPK().getLanguage();
}
public void setLanguageCode(Language language) {
getRelationLangPK().setLanguage(language);
}
and
public class RelationId implements Serializable {
private static final long serialVersionUID = 1L;
private Integer relationId;
private Language language;
#JoinColumn(name=PersistenseConstants.ELEMENT_RELATION_TYPE_COL_RELATION_ID)
public Integer getRelationId() {
return relationId;
}
public void setRelationId(Integer relationId) {
this.relationId = relationId;
}
#OneToOne
#JoinColumn(name=PersistenseConstants.LANGUAGE_ENTITY_COL_LANG_CODE)
public Language getLanguage() {
return language;
}
public void setLanguage(Language language) {
this.language = language;
}
You didn't post which errors do you receive, so I have to guess.
CriteriaBuilder.max accepts Expression<N> where N extends Number
At the same time Root.get by default returns Path<Object> which is inconvertible to Expression<Number>.
So to make your call to max work you need to specify generic parameter to root.get:
cq1.select(cb1.max(root.<Number>get("relationId")));
here you can replace Number with an actual type of relationId (Long, BigInteger etc.)
UPDATE: #perissf addressed another issue with your code. If you are going to select maximal value (which is numeric) you should declare your CriteriaQuery as a query to Number not ElementRelationTypes

Copy Entity ID at persist time

I want to copy the entity's UUID, generated at run time to another field.
The entity id is generated via the code described bellow:
package eclipselink.example;
public class UUIDSequence extends Sequence implements SessionCustomizer {
public UUIDSequence() {
super();
}
public UUIDSequence(String name) {
super(name);
}
#Override
public Object getGeneratedValue(Accessor accessor,
AbstractSession writeSession, String seqName) {
return UUID.randomUUID().toString().toUpperCase();
}
...
public void customize(Session session) throws Exception {
UUIDSequence sequence = new UUIDSequence("system-uuid");
session.getLogin().addSequence(sequence);
}
}
Persitence.xml:
property name="eclipselink.session.customizer" value="eclipselink.example.UUIDSequence"
The entity:
public abstract class MyEntity{
private String id;
private String idCopy;
#Id
#Basic(optional = false)
#GeneratedValue(generator="system-uuid")
#XmlElement(name = "ID")
public String getId() {
return id;
}
}
How can I instruct JPA (Eclipse-link) to copy the UUID generated at runtime to idCopy field as well?
I'm not 100% sure this will work (I don't know if EclipseLink calls the setter or assigns the field directly), but give this a try:
public abstract class MyEntity{
private String id;
private String idCopy;
#Id
#Basic(optional = false)
#GeneratedValue(generator="system-uuid")
#XmlElement(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
this.idCopy = id;
// or
// this.setIdCopy(id);
}
}