Wildfly - using an enum in an embeddable bean - java

i want to use an enum in an entity bean. But the enum is in an embedded object. There is the code:
#Entity
#Table(name = "entity_foo")
public class EntityFoo implements Serializable
{
#Embedded
private EmbeddedFoo embeddedFoo;
public EmbeddedFoo getEmbeddedFoo()
{
return embeddedFoo;
}
public void setEmbeddedFoo(final EmbeddedFoo embeddedFoo)
{
this.embeddedFoo = embeddedFoo;
}
}
My Embedded Object includes the enum and looks like this:
#Embeddable
public class EmbeddedFoo implements Serializable
{
public static enum EnumBar {
VALUEA,VALUEB
}
private EnumBar enumBar;
#Enumerated(EnumType.STRING)
public EnumBar getEnumBar()
{
return enumBar;
}
public void setEnumBar(final EnumBar enumBar)
{
this.enumBar = enumBar;
}
}
In the table entity_foo i declared the value enumbar as varchar(255). Now i try to get data from the database.
final List<EntityFoo> entityFoos = query.getResultList();
This throws an PSQLException:
Caused by: org.postgresql.util.PSQLException: Bad value for type int : VALUEA
If i use the enum directly in the entity "EntityFoo" it works fine. This code runs on Wildfly 8.1 with Postgres 9.3 and Java 1.7
I hope i declared my problem clearly and anyone can help me.
UPDATE
Here is the link to a miniproject repository
https://github.com/MotherCake/miniprojct
To use this project you need a table. Here is the create statement.
CREATE TABLE "public"."minitable"(id int PRIMARY KEY NOT NULL,enumbar varchar(255));
CREATE UNIQUE INDEX minitable_pkey ON "public"."minitable"(id);
INSERT INTO minitable (id,enumbar) VALUES (0,'VALUEA');
INSERT INTO minitable (id,enumbar) VALUES (1,'VALUEB');
What i forgot to mention is that i'm using Toplink. I think Toplink causes the problem.
In the class Testservlet you can see 2 different ways to create a query. The exception i described is thrown if i use the createQuery(..) method with the setParameter(..) method. This is in the comment.
If i'm using the method createNativeQuery i get the result list and no exception is thrown. But after that i get a ClassCastException in class TestServlet.

The problem is that by default enums are persisted as int using oridinal. You have to change it to String representation by adding #Enumerated(EnumType.STRING) on enum field. Also check that in your main entity you've annotated fields instead of getter/setter so it turns on FIELD access on the entity as well as on embedded entity (in this case EmbeddedFoo). So if you annotate in EmbeddedFoo field instead of getter it will works correctly.
#Embeddable
public class EmbeddedFoo implements Serializable
{
public static enum EnumBar {
VALUEA,VALUEB
}
#Enumerated(EnumType.STRING)
private EnumBar enumBar;
public EnumBar getEnumBar()
{
return enumBar;
}
public void setEnumBar(final EnumBar enumBar)
{
this.enumBar = enumBar;
}
}
Also if you must have annotation on the getter for some reason you have to annotate EmbeddedFoo by #Access(AccessType.PROPERTY) and it'll also fix your problem.
#Embeddable
#Access(AccessType.PROPERTY)
public class EmbeddedFoo implements Serializable
{
public static enum EnumBar {
VALUEA,VALUEB
}
private EnumBar enumBar;
#Enumerated(EnumType.STRING)
public EnumBar getEnumBar()
{
return enumBar;
}
public void setEnumBar(final EnumBar enumBar)
{
this.enumBar = enumBar;
}
}

I found a solution to get the project work, but i dont know why this is necassary.
I thought, as Jakub wrote, that adding #Enumerated(EnumType.STRING) should be enough.
I annotated the variable enumBar with #Column(name = "enumbar").
#Column(name = "enumbar")
#Enumerated(EnumType.STRING)
public EnumBar enumBar;
Furthermore i add the #AtributeOverride annotation to the embedded object in EntityFoo
#Embedded
#AttributeOverride(name = "enumBar", column = #Column(name = "enumbar"))
private EmbeddedFoo embeddedFoo;
After that it works like charm.
I update the project on github for everyone who would like to try out.
Thanks for help.

Related

Saving entity with Spring JPA Repository resets enum ttype value

I have an TimelineEntity entity, that uses HoTimelineType enum with custom integer value. That custom integer value is stored in the database. Implemented via Using #PostLoad and #PrePersist Annotations
Sprint JPA Repository is used to save and get entities.
Here is the issue:
#Entity
#Table(name = TABLE_NAME)
#IdClass(TimelineKey.class)
public class TimelineEntity {
public interface Persistence {
String TABLE_NAME = "timelines";
}
#Id
#Column(name = "node_id")
private Long nodeId;
#Id
#Column(name = "timeline_id")
private Long timelineId;
#Column(name = "ho_timeline_type")
private Integer hoTimelineTypeValue;
#Transient
private HoTimelineType hoTimelineType;
public Long getNodeId() {
return nodeId;
}
public void setNodeId(Long nodeId) {
this.nodeId = nodeId;
}
public Long getTimelineId() {
return timelineId;
}
public void setTimelineId(Long timelineId) {
this.timelineId = timelineId;
}
public HoTimelineType getHoTimelineType() {
return hoTimelineType;
}
public void setHoTimelineType(HoTimelineType hoTimelineType) {
this.hoTimelineType = hoTimelineType;
}
public Integer getHoTimelineTypeValue() {
return hoTimelineTypeValue;
}
public void setHoTimelineTypeValue(Integer hoTimelineTypeValue) {
this.hoTimelineTypeValue = hoTimelineTypeValue;
}
#PostLoad
private void postLoad() {
this.hoTimelineType = HoTimelineType.of(hoTimelineTypeValue);
}
#PrePersist
private void prePersist() {
this.hoTimelineTypeValue = hoTimelineType.getValue();
}
}
#Eager
public interface TimelineEntityRepository extends JpaRepository<TimelineEntity, TimelineKey> {
List<TimelineEntity> findByNodeId(Long nodeId);
}
#Autowired
private TimelineEntityRepository timelineEntityRepository;
...
TimelineEntity newTE = new TimelineEntity();
newTE.setNodeId(10L);
newTE.setTimelineId(22L);
newTE.setHoTimelineType(HoTimelineType.TYPE_1);
newTE = timelineEntityRepository.save(newTE);
When the newTE entity is saved, prePersist is invoked, and inside this method, the hoTimelineType is null and I get NPE. nodeId and timelineId are not nulls. If I stay with a debugger on the last line, outside of prePersist, I see that hoTimelineType has the value, I set before.
When I load entities, inserted with test data, everything works fine and both hoTimelineType and hoTimelineTypeValue have not nullable values.
I skipped the code of TimelineKey and HoTimelineType to simplify the example. Can add it, if needed.
What could reset hoTimelineType? What do I miss?
It seems there is no way to control the saving behaviour of spring jpa repository proxy.
Possible solutions for issue:
Via javax.persistence.Converter. It is pretty clear, the structe of an entity is simple. Can confirm it works fine with Spring Jpa Repository generation.
Explicitely set hoTimelineTypeValue before you save an entity. Error-prone solution. Everytime you save an entity you must think about the difference between the hoTimelineTypeValue and hoTimelineType.
You could enrich setters and getters of the entity class, to explicitely control the consistency between the fields. It makes implementation of entity classes not so obvious. You get more compicated solution for nothing. As a result error-prone solution. Do not recommend it as well.
Cause of disadvantages of #2 and #3 I do not provide examples. It makes no sense.
Example of the solution #1 can be found here: Using JPA 2.1 #Converter Annotation

Query using value from nested interface as parameter

I have been reading a lot about using enums as parameters in queries. I have some queries in my project that use the value from these enums as parameters.
For example:
public enum YesNo {
Y, N
}
Query:
select ent
from
Entity ent
where
ent.id = :id
and ent.deleted = project.path.example.YesNo.N
Entity:
#Entity
public class Entity{
Long id;
#Enumerated(EnumType.STRING)
YesNo deleted;
}
The above works correctly as expected.
However, when I have the following:
interface Commons{
interface MostCommonTypesofAnimals {
long DOG = 1L;
long CAT = 2L;
}
}
Query
select a
from
Animal a
where
a.id = :id
and a.type = project.path.example.Commons.MostCommonTypesofAnimals.DOG
Entity
#Entity
public class Animal{
Long id;
Type type;
}
#Entity
public class Type{
public Long id;
}
It does not work telling me that the path is incorrect even though it is actually correct.
Is there any work around? Or interface values cannot be mapped? Can anyone provide me an example that works? I could not find anything similar.
Please note that this is just an example to illustrate the situation., those are not the real names that I am using or anything.
For using enum while using hibernate / jpa (based on your tags), you should use annotation in your Pojo class.
#Enumerated(EnumType.ORDINAL)
In your example, something like:
#Entity
#Table(name = "tableName")
public class entityName {
#Enumerated(EnumType.ORDINAL)
private YesNo yesNoEnum;
}
The annotation can go here or in the getter, as you prefer.
You can find more info here
ps: for yes or no I suggest you using a boolean value, not an enum

Difficulty creating relationship with abstract class and embedded attribute with JPA/Hibernate

I'm trying, but have not been successful so far, using the following classes with Hibernate.
#MappedSuperclass
#Embeddable
public abstract class Foo {
// atributes...
}
#Embeddable
public class Poo extends Foo {
// atributes...
}
#Entity
#Table
public class None {
// atributes...
#Embedded
private Foo foo;
// constructor
public None(Foo foo) {
this.foo = foo;
}
}
// example of save
None none = new None(Poo poo);
save(none);
Hibernate returns: Cannot instantiate abstract class or interface
Is it possible to perform this operation with JPA?
I ran into the same problem.
It seems like #embedable does not work with #DiscriminatorColumn. The only way I could get this to work is to use #DiscriminatorColumn, but treat the #embedable like a separate entity on the same table.
What this means is that the query will likely join the table to itself.
#Entity
#Table(name="tbComputers")
public class Computer{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public long id;
public String motherboard;
#OneToOne
#JoinColumn(name="id")
public CPU cpu;
}
#Entity
#DiscriminatorColumn(name="cpu_type")
#Table(name="tbComputers")
public abstract class CPU {
#Id
private Long id;
#Column(name = "cpu")
public String name;
public abstract String someProcessorSpecificMethod();
}
#Entity
#DiscriminatorValue("Intel")
public class Intel extends CPU {
#Override
public String someProcessorSpecificMethod() {
return "Intel";
}
}
#Entity
#DiscriminatorValue("AMD")
public class AMD extends CPU {
#Override
public String someProcessorSpecificMethod() {
return "AMD";
}
}
EDIT: After further testing I found that while this works for reading data, it does not for persisting. It will create a separate INSERT. It seems like it is not supported https://hibernate.atlassian.net/browse/HHH-1910. The alternative is to to split the table.

Any way to apply sort of a JPA AttributeConverter over an Embeddable class?

I'd like to post-process my embeddable class to convert it to another type
#Embeddable
public class Identity {
private Long id;
private String alias;
}
#Embeddable
public class virtualIdentities {
private Long id;
private List<String> aliases; //AttributeConverter applied from a ","-joined string
}
#Entity
public class Parent {
private Identity identity; //Works flawlessly
private VirtualIdentities vIdentities; //Works flawlessly but...
private List<Identity> vIdentities; //<- That is what I'd like to achieve!!
}
So I am looking for a mechanism (standard JPA preferrably) that allows me to map someway a VirtualIdentities embeddable instance as a List<Identity>
If only I could do AttributeConverter<List<Identity>,VirtualIdentities> ...

Creating other public methods in entity class

I have an Entity class
#Entity
#Table(name = "rule")
public class Rule implements Cloneable, Serializable, IPojoGenEntity, IRule, SequencedEntity {
private String name;
private Service service;
//getter .. setter for service and name
public String getServiceName() {
return (this.service.getName());
}
public void setServiceName(String servicename) {
this.service.setName(servicename);
}
}
I am getting exception for getting service name through RulClass object
public String getServiceName() {
return (this.service.getName());
}
Stack Trace
Caused by: com.ibm.db2.jcc.b.SqlException: "RULE0_.SERVICENAME" is not valid in the context where it is used.
at com.ibm.db2.jcc.b.fg.e(fg.java:1596)
at com.ibm.db2.jcc.b.fg.a(fg.java:1206)
at com.ibm.db2.jcc.a.gb.g(gb.java:140)
at com.ibm.db2.jcc.a.gb.a(gb.java:39)
at com.ibm.db2.jcc.a.w.a(w.java:34)
at com.ibm.db2.jcc.a.vb.g(vb.java:139)
Can we use such getter and setter in an entity class?
I am using hibernate, spring, DB2, IBM WebSphere
You should either make it #Transient as it was mentioned if you don't want to store it
OR
Define
#javax.persistence.Column(name = "service_id") field annotation for the getter to let hibernate know which column to use.
OR
Rename DB to have the service field "SERVICENAME" to use default column name
There is the Transient annotation to tell Hibernate to ignore a field. So:
#Transient
private Service service;
From very similar SO question: Make hibernate ignore class variables that are not mapped.
As serviceName was not a member of Rule class so there is a problem with method name. Name cannot be like
getServiceName
setServiceName
rather it should be something other than get or set prefix
fetchServiceName
addServiceName

Categories