Persisting #ElementCollection of #Embeddable (Google App Engine, datanucleus) - java

I am trying to persist a JPA entity with a collection of custom #Embeddable objects using the JPA2 #ElementCollection annotation. Simple example (both classes are enhanced by datanucleus):
#Entity
public class TestEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ElementCollection
private Set<TestEmbeddable> testEmbeddables;
public Set<TestEmbeddable> testEmbeddables() {
return this.testEmbeddables;
}
}
#Embeddable
public class TestEmbeddable implements Serializable {
public String s;
}
The persisted Datastore entity, however, will only contain a collection of null values instead of the actual objects:
TestEntity.testEmbeddables = [null, null, ...]
Persisting a collection of basic types such as Strings or embedding a single TestEmbeddable object using #Embedded works perfectly fine. Would someone be able to clarify whether element collections of embeddables are supported by datanucleus-appengine?
While the datanucleus section on JPA element collections only gives an example for a String collection, the corresponding JDO section uses a custom embedded-only type. The feature list further states that embedded collections in general are compatible with GAE, but does not say whether custom types are supported. I also found one other person claiming that this should work.
-- Edit --
Following DataNucleus' answer, I ran some more tests:
#ElementCollection
private List<String> stringsElementCollection;
--> Works. The individual Strings are persisted as TestEntity.stringsElementCollection = [str1, str2, ...]
#Embedded
private List<String> stringsEmbedded;
--> Same as #ElementCollection. I wonder if the JPA specification covers the use of #Embedded on a collection, though?
#ElementCollection
private List<TestEmbeddable> embeddablesElementCollection;
--> Doesn't work. Instead of the actual TestEmbeddable objects, the Datastore persists only a collection of null values: TestEntity.embeddablesElementCollection = [null, null, ...]
#Embedded
private List<TestEmbeddable> embeddablesEmbedded;
--> This seems to work. The TestEmbeddable.s field is stored as TestEntity.s.0, .s.1, etc. plus a TestEntity.embeddablesEmbedded.size property.
(App Engine SDK 1.7.7.1, datanucleus 3.1.3, datanucleus-appengine 2.1.2)

No idea if Google support embedded collections of such objects yet with JPA. That was only developed for JDO and works there. You could try putting a #Embedded on the embedded field to reinforce the idea that its embedded.

The issue has been recorded by David Geiger here:
https://code.google.com/p/datanucleus-appengine/issues/detail?id=318

Related

Cannot declare List property in the JPA #Entity class. It says 'Basic' attribute type should not be a container

I have a JPA #Entity class Place, with some properties holding some information about a place, such as name of place, description, and URLs of some images.
For the URLs of images, I declare a List<Link> in my entity.
However, I am getting this error:
Basic attribute type should not be a container.
I tried to remove #Basic, but the error message is still there. Why does it shows this error?
You can also use #ElementCollection:
#ElementCollection
private List<String> tags;
You are most likely missing an association mapping (like #OneToMany) and/or #Entity annotation(s).
I had a same problem in:
#Entity
public class SomeFee {
#Id
private Long id;
private List<AdditionalFee> additionalFees;
//other fields, getters, setters..
}
class AdditionalFee {
#Id
private int id;
//other fields, getters, setters..
}
and additionalFees was the field causing the problem.
What I was missing and what helped me are the following:
#Entity annotation on the generic type argument (AdditionalFee) class;
#OneToMany (or any other type of association that fits particular business case) annotation on the private List<AdditionalFee> additionalFees; field.
So, the working version looked like this:
#Entity
public class SomeFee {
#Id
private Long id;
#OneToMany
private List<AdditionalFee> additionalFees;
//other fields, getters, setters..
}
#Entity
class AdditionalFee {
#Id
private int id;
//other fields, getters, setters..
}
Change #basic to #OneToMany for List types
Or you can mark it as #Transient if it doesn't exist on DB table.
#Transient
private List<String> authorities = new ArrayList<>();
As the message says, #Basic should not be used for containers (e.g. Java collections). It is only to be used for a limited list of basic types. Remove the #Basic annotation on that field.
If, as you say in the question, the error message is still there, you might need to try the following steps in order:
Save the file
Close and reopen the file
Clean and rebuild the project
Restart the IDE
(these are generic steps, which I use when an IDE is generating a compilation error that obviously makes no sense.)
This can also happen when your class is missing its #Entity annotation. When you get weird warnings like these, sometimes it helps to try and compile and see if the compiler complains.
The error seems not have impact on GAE since I can run the app and store data into storage. I guess it's a bug in IntelliJ IDEA and you can simply ignore it.
'Basic' attribute type should not be a container
This error occurs when you declare an existing entity as an attribute in the current Entity without declaring the relationship type which could be either of the JPA relationships.
Detailed Article on JPA relationships

Why is this field not being serialized?

My Issue entity was created from a DB table that has several fields (id, etc...). Each issue has as a field a list of Articles, which are stored in a separate DB table. Articles have a int issueID field, which is used to map them to the appropriate Issue (there is no corresponding column in the issues table): Ultimately, when an Issue object is constructed, I'm going to have it pull all of the articles whose issueID matches its ID, so that I can return a single serialized object that contains the issue data as well as a JSONArray representing its list of articles.
At this point, though, I'm just doing some testing - creating a few dummy Article objects and adding them to the articles collection. The problem is that, when I test GET requests on the Issue object, the JSONObject returned includes only the fields stored in the database (id, etc...) - no sign of the Article collection. Why is that?
I'm equally interested to know what other code you would need to see to answer this question: I've just begun teaching myself how to write web services and am still in the phase of wrapping my head around the broad concepts, so figuring out which of the moving parts has affects which behaviors - and which annotations are needed where - is ultimately what I'm trying to do.
That being the case, broader-based advice is welcomed.
#Entity
#Table(name = "issues")
#XmlRootElement
public class Issue implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id")
private Integer id;
....//other fields
#OneToMany(mappedBy = "issueID")
private Collection<Articles> articlesCollection;
public Issue() {
articlesCollection = new ArrayList<Articles>();
Articles a = new Articles();
a.setHeadline("butt cheese");
articlesCollection.add(a);
Articles b = new Articles();
articlesCollection.add(b);
Articles c = new Articles();
articlesCollection.add(c);
}
By default the relationship initialization is lazy so when the Issue object is loaded the articlesCollection is not fetched unless used.
In your case seems its the same situation.
Explore OpenEntityManagerInViewFilter if you do not intend to explicitly load articlesCollection. When your object serializes the articlesCollection will be loaded if you have configured OpenEntityManagerInViewFilter.
Does your Articles Class also has #XmlType or #XMLRootElement Tag?
Onany generic class like List<T> jaxb expects that T is annotated with #XMLType or #XMLRootElelemt

Hibernate Search not indexing embedded collections properly

I'm currently working on a project that involves the use of Hibernate Search. Currently the project uses pure SQL for its searches and we would like to use a text search instead (Needing to know and correctly spell the first word can get annoying).
The schema is that a product can have multiple versions and the current version contains the name of the product.
Public Class Product extends ProgEntity
{
private List<ProductVersion> versions = new ArrayList<ProductVersion>();
...
}
Public Class ProductVersion extends ProgEntity
{
String productName;
...
}
I need to be able to search for a product based on its name. I was able to index the ProductVersions by productName with little issue however indexing Product is proving to be more of an issue.
After some research this is what I have however when I update the product to the DB no index is created.
#Entity
#Indexed
Public Class Product extends ProgEntity
{
#IndexedEmbedded
private List<ProductVersion> versions = new ArrayList<ProductVersion>();
...
}
#Entity
#Embeddable
Public Class ProductVersion extends ProgEntity
{
#Field
String productName;
...
}
The DocumentID is part of ProgEntity. I need to be sure that if I update Product or Product Version that it will be indexed properly which does not seem to be happening now.
Any suggestions on what I am doing incorrectly?
You don't have a relationship (eg many-to-one, many-to-one) between Product and ProductVersion mapped in the code you posted. This relationship must be bi-directional. Annotate the Product's collection field with #IndexedEmbedded, and the inverse field on the ProductVersion side with #ContainedIn, and you should be all set.
Using #Entity and #Embeddable on ProductVersion seems wrong. There are also some JPA annotations missing. Is the version collection mapped as #ManyToOne or #ElementCollection.
Have you checked your hibernate configuration and log files? Which directory provider are you using?

EJB3/JPA entity with an aggregated attribute

I wanted to know if there is a way to get in a One2Many relationship a field of the One side that is an aggregate of the Many side.
Let's take the following example:
#Entity
public class A {
#Id
private Long id;
#OneToMany (mappedBy="parentA")
private Collection<B> allBs;
// Here I don't know how to Map the latest B by date
private B latestB;
// Acceptable would be to have : private Date latestBDate;
}
#Entity
public class B {
#Id
private Long id;
private Date date;
#ManyToOne (targetEntity=A.class)
private A parentA;
}
My question is how can I make the mapping of the field latestB in the A entity object without doing any de-normalization (not keeping in sync the field with triggers/listeners)?
Perhaps this question gives some answers, but really I don't understand how it can work since I still want to be able to fetch all childs objects.
Thanks for reading/helping.
PS: I use hibernate as ORM/JPA provider, so an Hibernate solution can be provided if no JPA solution exists.
PS2: Or just tell me that I should not do this (with arguments of course) ;-)
I use hibernate as ORM/JPA provider, so an Hibernate solution can be provided if no JPA solution exists.
Implementing the acceptable solution (i.e. fetching a Date for the latest B) would be possible using a #Formula.
#Entity
public class A {
#Id
private Long id;
#OneToMany (mappedBy="parentA")
private Collection<B> allBs;
#Formula("(select max(b.some_date) from B b where b.a_id = id)")
private Date latestBDate;
}
References
Hibernate Annotations Reference Guide
2.4.3.1. Formula
Resources
Hibernate Derived Properties - Performance and Portability
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Filtering.2C_Complex_Joins
Basically JPA does not support this, but some JPA providers do.
You could also,
- Make the variable transient and lazy initialize it from the OneToMany, or just provide a get method that searches the OneToMany.
- Define another foreign key to the latest.
- Remove the relationship and just query for the latest.

Does JPA support mapping to sql views?

I'm currently using Eclipselink, but I know now days most JPA implementations have been pretty standardized. Is there a native way to map a JPA entity to a view? I am not looking to insert/update, but the question is really how to handle the #Id annotation. Every entity in the JPA world must have an ID field, but many of the views I have created do not conform to this. Is there native support for this in the JPA or do I need to use hacks to get it to work? I've searched a lot and found very little information about doing this.
While using the #Id annotation with fields of directly supported types is not the only way to specify an entity's identity (see #IdClass with multiple #Id annotations or #EmbeddedId with #Embedded), the JPA specification requires a primary key for each entity.
That said, you don't need entities to use JPA with database views. As mapping to a view is no different from mapping to a table from an SQL perspective, you could still use native queries (createNativeQuery on EntityManager) to retrieve scalar values instead.
I've been looking into this myself, and I've found a hack that I'm not 100% certain works but that looks promising.
In my case, I have a FK column in the view that can effectively function as a PK -- any given instance of that foreign object can only occur once in the view. I defined two objects off of that one field: one is designated the ID and represents the raw value of the field, and the other is designated read-only and represents the object being referred to.
#Id
#Column(name = "foreignid", unique = true, nullable = false)
public Long getForeignId() {
...
#OneToOne
#JoinColumn(name = "foreignid", insertable=false, updatable=false)
public ForeignObject getForeignObject() {
...
Like I said, I'm not 100% sure on this one (and I'll just delete this answer if it turns out not to work), but it got my code past a particular crash point.
Dunno if it applies to your specific situation, though. And there's an excellent chance that after 11 months, you no longer care. :-) What the hell, that "Necromancer" badge doesn't just earn itself....
In my view I have a "unique" id, so I mapped it as the Entity id.
It works very well:
#Entity
#Table(name="table")
#NamedQuery(name="Table.findAll", query="SELECT n FROM Table n")
public class Table implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="column_a")
private int columnA;
JPA - 2.5.4
CREATE MATERIALIZED VIEW IF NOT EXISTS needed_article as select product_id, count(product_id) as count from product_article group by product_id;
CREATE MATERIALIZED VIEW IF NOT EXISTS available_article as select product_id, count(product_id) as count from article a inner join product_article p
on a.id = p.article_id and a.stock >= p.amount_of group by product_id;
CREATE UNIQUE INDEX productId_available_article ON available_article (product_Id);
CREATE UNIQUE INDEX productId_needed_article ON needed_article (product_Id);
Entity.java
#Entity
#Immutable // hibernate import
#Getter
#Setter
public class NeededArticle {
#Id
Integer productId;
Integer count;
}
Repository.java
#Repository
public interface AvailableProductRepository extends CrudRepository<AvailableArticle, Integer> {
#Query("select available.productId from AvailableArticle available, NeededArticle needed where available.productId = needed.productId and available.count = needed.count")
List<Integer> availableProduct();

Categories