Hi Ted -
Interesting point of view. I would have thought this is the plain
vanilla "simple clean out of the box" way to persist a Collection of
Enums or any other kind of collection.
This results in a bare minimum join with two tables and a foreign key
which is how a Collection maps into the world of relational databases
whether you are using straight JDBC or JPA or Hibernate or brand X ORM.
In the end you wind up with two database tables and two objects that
model the association.
- Paul
On 3/31/2009 7:40 AM, Tedman Leung wrote:
thanks but I was hoping there was a simple clean out of the box style
solution. Considering the code could/should have just been a single
annotation to the collection, the work around looks like an awful lot of
work for a framework which is suppose to reduce work.
In the end, I just used raw JDBC in a nativeQuery. At least this way other
developers can understand the code I've written and it's only actually
about 15 lines of simple straight forward code.
thanks.
On Mon, Mar 30, 2009 at 11:16:52AM -0700, Paul Copeland wrote:
Hi Ted - See example code below
On 3/30/2009 9:47 AM, Tedman Leung wrote:
Isn't the same thing true that OneToMany should annotate a Collection
of Entities? So the Enums would be a field on the ManyToOne side of
that kind of mapping. Then you can specify the @Column and
@Enumerated annotations on the actual field or property.
no you can't, I tried it. I can't remember the specific error but it
was along the lines of - you're not allowed to specify a column
attribute here.
Here is an example that uses the @Enumerated and @Column annotations on
a OneToMany relation.
This stores a Set of Enums as String values. A similar example would
work for ManyToMany. The same approach can also be used with
Collections of Temporal values.
There are two classes - an EnumHolder that holds the Collection and an
EnumValue that holds an Enum and stores it as a String. Here is the
MappingTool generated SQL (Postgres database). As you can see the Enum
is stored as a String VARCHAR(10).
CREATE TABLE enum_holder (id BIGSERIAL NOT NULL, PRIMARY KEY (id));
CREATE TABLE enum_value (id BIGSERIAL NOT NULL, StringEnum VARCHAR(10),
ENUMHOLDER_ID BIGINT, PRIMARY KEY (id));
package com.jpa.test;
import javax.persistence.*;
import java.util.*;
@Entity
@Table (name="enum_holder")
public class EnumHolder
implements java.io.Serializable
{
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Id private long id;
@OrderBy
@OneToMany(mappedBy="enumHolder", fetch=FetchType.LAZY,
cascade={CascadeType.PERSIST,CascadeType.REMOVE})
private Set<EnumValue> enums;
public Set<EnumValue> getEnums()
{
if (enums == null)
enums = new HashSet<EnumValue>();
return enums;
}
}
package com.jpa.test;
import javax.persistence.*;
@Entity
@Table (name="enum_value")
public class EnumValue
implements java.io.Serializable
{
public static enum MyEnum{foo, Bar, Batz, woohoo}
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Id private long id;
@ManyToOne (optional=false, fetch=FetchType.LAZY,
cascade=CascadeType.PERSIST)
private EnumHolder enumHolder;
@Enumerated (EnumType.STRING)
@Column(name="StringEnum", length=10)
private MyEnum myEnum;
public MyEnum getMyEnum() { return myEnum; }
}
On 3/30/2009 9:47 AM, Tedman Leung wrote:
How does your original example of ManyToMany annotation on a
Collection of Enums actually work? My understanding is that for
ManyToMany both sides must be persistent Entities. So therefore the
ManyToMany is mapping the join table, not the Enum.
yes oddly enough it actually does work. As per my subsequent email, I
switched it to @PersistentCollection and it performed the same way. I
considered trying to annotate my Enum like an Entity but I figured that
would be pushing expected behaviour... even if it worked, so I decided
not to.
Isn't the same thing true that OneToMany should annotate a Collection
of Entities? So the Enums would be a field on the ManyToOne side of
that kind of mapping. Then you can specify the @Column and
@Enumerated annotations on the actual field or property.
no you can't, I tried it. I can't remember the specific error but it
was along the lines of - you're not allowed to specify a column
attribute here.
Could you explain why using the @PersistentCollection annotation is
useful or necessary for OneToMany or ManyToMany collections generally
or specifically in the case you are looking at? I don't understand
the use case that @PersistentCollection satisfies.
My understanding is @ManyToMany is a JPA standard and is only used to
map entity to entities.
My understanding of @PersistentCollection is that is is an OpenJpa
extention which allows people to map Entity to java Primities.
i.e.
@Entity
class Bar
{
}
@Entity
class Foo
{
@ManyToMany
HashSet<Bar> myBars=new HashSet<Bar>();
@PersistentCollection
HashSet<String> myStrings=new HashSet<String>();
}
I guess there's a reason why the original JPA standard left out
primitives... it gets complicated fast.
- Paul
On 3/29/2009 8:43 PM, Tedman Leung wrote:
Just to document more thoughts and findings.
1) I was using the wrong annotation, I should have been using
@PersistentCollection
2) this seems to be a more wide spread problem with defining
attributes to collections, i.e. - collection of enums needs to have
@Enumerated configurations
- collection of Dates needs to have @Temporal configurations
- even collection of Strings seems to be missing a
@Column(nullable=false, length=32) style annotation.
I think the problem is much bigger and wide spread than I originaly though,
I don't think I'll be able to sort out a patch.
On Sun, Mar 29, 2009 at 07:55:06AM -0700, Tedman Leung wrote:
yes I know I can do manual hacks to get them stored the way I
want to, but I was asking if there was a jpa / openjpa annotation
to allow that.
As an example, I can just use @Enumerated on a single enum, so it
seems logical that some one would have thought about storing a
collection of enums.
As for why I want them as strings instead of ordinals - the usual
reasons, i.e. it's safer for changes as the ordering won't
accidentally corrupt my data (and without telling me), and it's
easier to look into the database via sql or something and just
know what's going on / get simple reports etc.
I'm guessing right now that it's not possible at all and that no
one has done anything towards this. I might have to rummage
through the source and try to see if I can figure out how it
works and hack a patch to submit. It seemed like a very straight
forward use case though so I'm surprised no one has asked or done
anything about this before.
On Sat, Mar 28, 2009 at 10:13:16PM -0700, Paul Copeland wrote:
What is your objective? Do you want some non-JPA application
to see them in the database as Strings?
At some point you have add these Enums to the collection one at
a time. You can use an addEnum() method or an Entity listener
to convert them to Strings at that point one at a time. And a
subclass of the Collection type to getEnum() from the
Collection when you fetch them back.
new MyStringEnumHashSet<MyStringEnumType>()
On 3/28/2009 9:27 PM, Tedman Leung wrote:
No, I'm talking about when the enum is in a collection.
i.e.
Private HashSet<Gender> genders=new HashSet<Gender>();
So no, either the @Enumerated helps, nor does calling name()
or toString as neither are possible.
I'm storing a Collection of enums , not a single Enum.
There seems to be no examples of this nor any documentation
about the ability to do this that I can find. The default
seems to use the ordinal value both for table generation and
storage value.
On Sat, Mar 28, 2009 at 01:56:13PM -0700, Paul Copeland wrote:
Hi - This is from the OpenJPA relations example -
@Basic @Enumerated(EnumType.STRING)
private Gender gender;
public static enum Gender { MALE, FEMALE }
public void setGender(Gender gender) {
this.gender = gender;
}
See section 12.8.1.2 in the OpenJPA Overview
- Paul
On 3/28/2009 1:33 PM, catalina wei wrote:
Ted,
If you are using Java 5, then you could use name() or toString() API on the
Enum to get the name of the enum constant in String value.
Catalina
On Wed, Mar 25, 2009 at 9:48 AM, Tedman Leung <[email protected]> wrote:
Anyone know how to store a collection of enums as Strings instead of their
ordinal values? (preferably with annotations...)
i.e.
@ManyToMany
private Set<MyEnum> myEnums=new HashSet<MyEnum>();
--
Ted Leung
[email protected]
It's time for a new bike when the bulb in your shift light burns out.
--
Ted Leung
[email protected]
The most important words I've learned to say - "I don't know".