Load the embedded class directly without the embedding class in Morphia - java

I've the following two simple classes, which are exemplary for the structure of my problem:
The first class, which embeds the second one
#Entity
public class MyClass {
#Id
private String myClassName;
private String otherField;
#Embedded
private List<MyEmbedded> myEmbeddeds;
}
And the second class which will be embedded:
#Embedded
public class MyEmbedded {
#Id
private String name;
private String some;
private String other;
}
In the real case, both classes have a far more complicated structure, with a lot of more fields and references.
Due to that, i don't want to load the whole MyClass object, as in most cases I only need one specific element from the MyEmbedded list (in most cases with a read-only access).
On the other hand, setting the MyEmbedded class as a simple reference is no option, as we have some complex queries for the MyClass which heavily depend on the myEmbeddeds, which would mean that we would have to execute multiple queries, which is not wanted.
So, the main question is:
How can I load one specific element of the myEmbeddeds list directly as a MyEmbedded-object, without loading the "parent"-object?
Maybe there is a way by using the AggregationPipeline? ( you can define a "target" class in pipeline.aggregate() method and one can find some examples in the tests of morphia as you can see here but i didn't get that working for my case)

You could query MyClass based on attributes of MyEmbedded and then use a projection to only pull myEmbeddeds from the results.

Related

How to map class attributes with underscores "_" in their names in Spring-data JPA

Does JPA with Spring-Data have a problem with attributes with underscores "_" in their names? This is my interface that extends JpaRepository:
public interface I_My_Class extends JpaRepository<MyClass, Long> {
public MyClass findByA_my_table_id (Long headerId);
}
This line: findByA_my_table_id (Long headerId); gives this error:
Invalid derived query! No property "a" found for type MyClass !
If I name the method public MyClass findBya_my_table_id (Long headerId); it gives me the same error. If I name the attribute amytableid without the underscores I don't get the error but if I do that it's not easy to read later on. This is the class where I have the table attribute:
#Entity
#Table(name="MyTable")
public class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column (name="MY_TABLE_ID", nullable=false)
private Long a_my_table_id; // <-- this is the attribute that I try to put in the named query
}
Yes Spring Data will have problem with underscores in Entity attribute names. Reason being JpaRepository simply expects attributes with proper Java Standard naming conventions eg property names should be in lower case. (if you can add multiple nouns to make it more meaning full then better make first letter of nouns in upper case except the first one)
String aMyTableId;
Above property would create tell JpaRepository to create a method like
List<MyClass> findByAMyTableId(String aMyTableId);
This would not give compilation error.
In case you want to write custom queries then you can use #Query API. Here you can write Object oriented query.
#Query("Select myclass from MyClass myclass where myclass.aMyTableId=?1 and myclass.activeFlag='1'")
List<MyClass> findByAMyTableIdWithActiveFlagOn(String aMyTableId);
You can find many tutorials and sites explaining how to write custom queries.

Persisting third-party classes with no ID's

Say I have the following Java class, which is owned by a vendor so I can't change it:
public class Entry {
private String user;
private String city;
// ...
// About 10 other fields
// ...
// Getters, setters, etc.
}
I would like to persist it to a table, using JPA 2.0 (OpenJPA implementation). I cannot annotate this class (as it is not mine), so I'm using orm.xml to do that.
I'm creating a table containing a column per field, plus another column called ID. Then, I'm creating a sequence for it.
My question is: is it at all possible to tell JPA that the ID that I would like to use for this entity doesn't even exist as a member attribute in the Entry class? How do I go about creating a JPA entity that will allow me to persist instances of this class?
EDIT
I am aware of the strategy of extending the class and adding an ID property it. However, I'm looking for a solution that doesn't involve extending this class, because I need this solution to also be applicable for the case when it's not only one class that I have to persist, but a collection of interlinked classes - none of which has any ID property. In such a scenario, extending doesn't work out.
Eventually, I ended up doing the following:
public class EntryWrapper {
#Id
private long id;
#Embedded
private Entry entry;
}
So, I am indeed wrapping the entity but differently from the way that had been suggested. As the Entry class is vendor-provided, I did all its ORM work in an orm.xml file. When persisting, I persist EntryWrapper.
I don't have much experience with JPA, but I wouldn't extend your base classes, instead I would wrap them:
public class PersistMe<T> {
#Id
private long id;
private T objToWrap;
public(T objToWrap) {
this.objToWrap = objToWrap;
}
}
I can't test it, if it doesn't work let me know so I can delete the answer.

Morphia - change class associated with a collection

I'm trying to phase out an older java codebase that uses MongoDB/Morphia. During this transition, I'd like the new platform to write to the same MongoDB database/collections so that each can live side by side for a little while. That part I'm doing alright with. My issue is that in the new platform, I need a different package/class structure for the objects I'm mapping with morphia than what is currently in the collection.
For instance, in the old platform I've got this class:
package com.foo;
#Entity
public class Bar {
#Id private String id;
private String name;
...
}
In my mongo database, I now have a collection "Bar" and its documents have the className attribute set to "com.foo.Bar". That's all wonderful.
What I'd like to do in the new platform is create a brand new class in a different package to represent that entity, but have it interact with mongo in the same way. I'm hoping to be able to do something like this:
package com.foo.legacy;
#Entity("com.foo.Bar")
public class LegacyBar {
#Id private String id;
private String name;
...
}
I realize the above doesn't work, but if I change the annotation to #Entity("Bar") I don't get any errors, but when I look up entities by id, I always get null back.
So... is there any way for me to have 2 separate VMs with 2 class structures and 2 different configurations of Morpha such that each can write to the same database/collection in the same fashion?
If I change LegacyBar to just "Bar" and create it in a package called "com.foo" then everything works as expected. I would just REALLY prefer to have the flexibility to quarantine all of this legacy data in a semi-clean fashion.
Do you even need the className attribute?
You can disable it with
#Entity(value = "Bar", noClassnameStored = true)
and drop the attribute in the database.
Quoting the official documentation:
Why would you need it?
This is mainly used when storing different
entities in the same collection and reading them back as the base or
super class.
If you don't do this, it should be an easy workaround to allow different package structures.

Extend entity classes with composite keys in hibernate

In our company we have a strange database model which can't be modified because to many systems works with them. Up to know we have a straight java application which connects with hibernate to the database and loads the data. We have for each table one xml mapping file.
The strange thing about the database is that we do not have any primary keys. Most table have a unique index containing several columns.
Now we want to use an application server (jboss) and the ejb model. So I created a class like this:
#Entity
#Table (name = "eakopf_t")
public class Eakopf implements Serializable {
#Embeddable
public static class EakopfId implements Serializable {
private String mandant;
private String fk_eakopf_posnr;
// I removed here the getters and setters to shorten it up
}
#Id
private EakopfId id;
private String login;
// I removed the getters and setters here as well
}
This works perfect.
Because our customers have different versions of the database schema I thought about extending this class on each database release change. So each interface we create with java can decide which version of the table will be used.
Here is the extended table class
#Entity
#Table (name = "eakopf_t")
public class Eakopf6001 extends Eakopf implements Serializable {
private String newField;
// getters and setters
}
If I use Eakopf (the base version) it is working if I do something like that:
EakopfId id = new EakopfId();
id.setMandant("001");
id.setFk_eakopf_posnr("ABC");
Eakopf kopf = (Eakopf) em.find(Eakopf.class, id);
But if I do this:
EakopfId id = new EakopfId();
id.setMandant("001");
id.setFk_eakopf_posnr("ABC");
Eakopf6001 kopf = (Eakopf6001) em.find(Eakopf6001.class, id);
this exception occues
javax.ejb.EJBException: javax.persistence.PersistenceException:
org.hibernate.WrongClassException: Object with id:
de.entity.Eakopf$EakopfId#291bfe83 was not of the specified subclass:
de.entity.Eakopf (Discriminator: null)
Does anybody has an idea?
many greetings,
Hauke
Doing what you did means to Hibernate that you're storing two different kinds of entities in a single table. This is possible is you use a discriminator column. But if I understand correctly, you just want one kind of entity in the table : Eakopf6001. In this case, its base class should be annotated with #MappedSuperClass, not with #Entity.
I would suggest creating a class annotated with #MappedEntity (let's call it BaseEakopf), and two entities: EaKopf and EaKopf6001, each with their set of additional fields. Include one of the other of the entities in the list of mapped classes, depending on which one you want to use.
My personal opinion is that if you have multiple versions of your app, they should use the same entities, but with different fields. Your version control system would take care of these multiple versions, rather than your source code (i.e. have one set of source files per version of the app, rather than one single set of source files for all the possible versions).

GWT + GAE datastore Key and Text Java Error

I would like to create an application that saves and retrieves records to the GAE server. I followed the tutorial "Deploying to Google App Engine" http://code.google.com/webtoolkit/doc/latest/tutorial/appengine.html to get started.
I have the StockWatcher application working now, but in my application I need to store a String that can be large (>10KB). I read that I can't use a Java String type to store large strings and need to use the Text data type instead.
I think by Text, they mean: com.google.appengine.api.datastore.Text, but it would be nice to confirm this is correct. ???
Regardless, I can't get Text to work. After some research it appears both the types Key and Text can only be used in the server code and not the client code. It seems that this is because the source code is not available for these classes and GWT needs the source to create the JavaScript code on the client's computer. At least that my current working hypothesis as to why I'm getting the following errors:
21:52:52.823 [ERROR] [myapp] Line 15: The import com.google.appengine.api.datastore cannot be resolved
21:52:52.951 [ERROR] [myapp] Line 103: Key cannot be resolved to a type
21:52:53.011 [ERROR] [myapp] Line 106: Text cannot be resolved to a type
I use the following fields in a class in a shared folder.
shared/MyDataRecord
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key id;
#Persistent
private Text description;
MyDataRecord class in a shared folder because I wanted to use to send back all the fields in one get method return rather than multiple individual field get methods. Here's how I use MyDataRecord class in my server/DataServiceImpl.java class
public class DataServiceImpl extends RemoteServiceServlet implements DataService
{
...
#Override
public MyDataRecord getDataRecord() throws NotLoggedInException
{
...
I've seen some posted solutions suggest using non-standard, 3rd party libraries, like http://www.resmarksystems.com/code/. I couldn't get this one installed, but even if I could, I'd prefer a different solution. Storing Text must be such a common task that I'd prefer to solve this using what is considered a standard solution.
I could change my code to return each field in multiple get methods instead of an single return of a MyDataRecord instance. However, even if that works, that would be significantly more work and more difficult to maintain over time. However, if this is what is normally expected, then that's what I'll do.
I'd like to solve this using what is considered best practices by GWT and GAE. A simple example or tutorial would go a long way, but I can't find one.
Are there example programs/tutorials that show what GWT considers best practices for storing and retrieving large strings?
I am a newbie with both GWT and GAE (as well as web development), please consider this in any responses, thanks.
No Snark Please
The serializable POJO. Note the NotPersistent annotation for description
package com.my.project.shared;
#PersistenceCapable(identityType=IdentityType.APPLICATION,detachable="true")
public class MyParent implements Serializable {
#PrimaryKey
#Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
private Long id;
#NotPersistent //Note the NotPersistent annotation. GAE won't persist this value in big table
private String description;
}
The second POJO. Notice the package
package com.my.project.server;
#PersistenceCapable(identityType=IdentityType.APPLICATION,detachable="true")
public class MyChild implements Serializable{//Not really required to implement Serializable
#PrimaryKey
#Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent
private Long parentID;//Reference to the MyParent
#Persistent
private Text description;//The actual value of the description variable.
}
Notice the parent ID mapped in the child. While retrieving you will need to identify which child belongs to which parent.
In pseudo code
1) Load parent from DB
2) Identify child for this parent, and load it
3) Convert child.description->parent.description
4) Now you have a fully constructed parent POJO which is serializable. Send it to the UI
Just reverse the procedure on the way back from UI to GAE.
1) Define a NotPersistent field in your serializable POJO private String description
2) Define a new POJO on the server side which will have private Text description
3) When you persist/load the original POJO, retrieve the new POJO and populate the String description from the Text description

Categories

Resources