Morphia - change class associated with a collection - java

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.

Related

Load the embedded class directly without the embedding class in Morphia

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.

Mapping JSON object to Hibernate entity

I'm going to start a project of a REST application managed with Spring and with Hibernate for my model.
I know that Spring allows you to get Java object from the HTTP Request (with #Consumes(JSON) annotation). Is there any conflict if this Java object is also a Hibernate entities? And is nested object working (like #ManyToOne relation)?
Maven dependency
The first thing you need to do is to set up the following Hibernate Types Maven dependency in your project pom.xml configuration file:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
Domain model
Now, if you are using PostgreSQL, you need to use the JsonType from Hibernate Types.
In order to use it in your entities, you will have to declare it on either class level or in a package-info.java package-level descriptor, like this:
#TypeDef(name = "json", typeClass = JsonType.class)
And, the entity mapping will look like this:
#Type(type = "json")
#Column(columnDefinition = "json")
private Location location;
If you're using Hibernate 5 or later, then the JSON type is registered automatically by the Postgre92Dialect.
Otherwise, you need to register it yourself:
public class PostgreSQLDialect extends PostgreSQL91Dialect {
public PostgreSQL92Dialect() {
super();
this.registerColumnType( Types.JAVA_OBJECT, "json" );
}
}
The JsonType works with Oracle, SQL Server, PostgreSQL, MySQL, and H2 as well. Check out the project page for more details about how you can map JSON column types on various relational database systems.
Yes, this wouldn't be a problem and is actually a fairly common practice.
In the recent years I have come to realize that sometimes, however, it is not a good idea to always build your views based on your domain directly. You can take a look at this post:
http://codebetter.com/jpboodhoo/2007/09/27/screen-bound-dto-s/
It is also known as "Presentation Model":
http://martinfowler.com/eaaDev/PresentationModel.html
The idea behind that is basically the following:
Imagine you have the domain entry User, who looks like that :
#Entity
#Data
public class User {
#Id private UUID userId;
private String username;
#OneToMany private List<Permission> permissions;
}
Let's now imagine you have a view where you wanna display that user's name, and you totally don't care about the permissions. If you use your approach of immediately returning the User to the view, Hibernate will make an additional join from the Permissions table because event though the permissions are lazily loaded by default, there is no easy way to signal to the jackson serializer or whatever you are using, that you don't care about them in this particular occasion, so jackson will try to unproxy them (if your transaction is still alive by the time your object is put for json serialization, otherwise you get a nasty exception). Yes, you can add a #JsonIgnore annotation on the permissions field, but then if you need it in some other view, you are screwed.
That a very basic example, but you should get the idea that sometimes your domain model can't be immediately used to be returned to the presentation layer, due to both code maintainability and performance issues.
We were using such approach to simplify design and get rid of many dtos (we were abusing them too much). Basically, it worked for us.
However, in our REST model we were trying to do not expose other relations for an object as you can always create another REST resources to access them.
So we just put #JsonIgnore annotations to relations mappings like #OneToMany or #ManyToOnemaking them transient.
Another problem I see that if you still like to return these relations you would have to use Join.FETCH strategy for them or move transaction management higher so that transaction still exists when a response is serialized to JSON (Open Session In View Pattern).
On my opinion these two solutions are not so good.
You can map the json request without using any library at REST web-services (Jersy)
this sample of code:
This hibernate entity called book:
#Entity
#Table(name = "book", schema = "cashcall")
public class Book implements java.io.Serializable {
private int id;
private Author author; // another hibernate entity
private String bookName;
//setters and getters
}
This web-services function
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String addBook(Book book) {
String bookName=book.getName();
return bookName;
}
This is sample json request:
{
"bookName" : "Head First Java",
"author" : {
"id" : 1
}
}
Since you are just starting, perhaps you could use Spring Data REST?
This is the project: http://projects.spring.io/spring-data-rest/
And here are some simple examples:
https://github.com/spring-projects/spring-data-book/tree/master/rest
https://github.com/olivergierke/spring-restbucks
As you can see in the examples, there are no extra DTOs beyond the #Entity annotated POJOs.

Java annotation fields setter and getter how to

What I want to accomplish but don't understand really how it works its
#Table(name = "Categories")
public class Category extends Model {
#Column(name = "Name")
private String name;
}
With this code I want to annotation generate their respective setter and getter so I can use like
Category category = new Category();
category.setName("My Name");
category.save();
Whey I need setter and getter and not access/edit the value directly? Because some values has different treatment, like relations, and because I want to have fields that don't want to be edited. And I'm too lazy to do manually all the time this, with every model, also save a lot of work later to just put an annotation and set their field
My inspiration to try this was Android Annotations seems a solid and cool library, I know maybe its too advanced but my goal with this experiment its to have a library like that but focused on models like active record or another orm.
Tuts, tips, advices, books are welcome.
Regards
Edit 2013-10-25
My goal is to build a library capable to do this, because I'm too curious and want to learn how internally work, so I'll be able to power my framework with this feature, as jet just are small utilities but in the future I hope it save me a lot of work, you can see at github WSD Android
If you are too lazy to create the setters and getters of your variables why not just let your IDE generate it for you?
but if you do really insist, take a look at this plugin
This simply allows you to do this
#Table(name = "Categories")
public class Category extends Model {
#Setter
#Getter
#Column(name = "Name")
private String name;
}
WARNING.
the plugin is not well documented, you also need to configure your IDE to actually see it(eg category.getName())

Can Hibernate be used to store HashMaps of data without classes to represent their structure?

I have pretty much zero experience with Hibernate, though I've used similar persistence libraries in other languages before. I'm working on a Java project that will require a way to define "models" (in the MVC sense) in text configuration files, generate the database tables automatically, and (ideally) be database-backend-agnostic. As far as I can tell from some quick Googling, Hibernate is the only widely-used backend-agnostic Java database library; while I could write my own compatibility layer between my model system and multiple DB backends, that's a debugging endeavor that I'd like to avoid if possible.
My question is: Can Hibernate be used to store data whose structure is represented in some other way than an annotated Java class file, such as a HashMap with some configuration object that describes its structure? And, if not, are there any other relatively-stable Java database libraries that could provide this functionality?
EDIT: Here's a clearer description of what I'm trying to accomplish:
I am writing a data-modeling library. When a certain method of the library is called, and passed a configuration object (loaded from a text file), it should create a new "model," and create a database table for that model if necessary. The library can be queried for items of this model, which should return HashMaps containing the models' fields. It should also allow HashMaps to be saved to the database, provided their structure matches the configuration files. None of these models should be represented by actual compiled Java classes.
I think you could try use #MapKey annotation provided by JPA (not the Hibernate #MapKey annotation, it's pretty different!).
#javax.persistence.OneToMany(cascade = CascadeType.ALL)
#javax.persistence.MapKey(name = "name")
private Map<String, Configuration> configurationMap = new HashMap<String, Configuration>();
I don't believe Hibernate will let you have a Map as an #Entity but it will let you have a custom class that contains a map field:
#Entity
public class Customer {
#Id #GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
#OneToMany #JoinTable(name="Cust_Order")
#MapKeyColumn(name"orders_number")
public Map<String,Order> getOrders() { return orders; }
public void setOrders(Map<String,Order> orders) { this.orders = orders; }
private Map<String,Order> orders;
}
(example from Hibernate docs)
Additionally, you don't have to use annotations (if that is what you're trying to avoid): Hibernate relationships can be described via xml files and there are utilities (maven plugins for example) which can automatically generate the necessary java pojo's from the xml.
Does your model require a relational database? You might consider a database like Mongo that stores any object you can represent with JSON.
you can configure hibernate to work without any entity classes (beans linked to tables),
1. you need to use xml configuration for this. in place of class use entity-name and in place of <property name="something" use <property node="something".
create a hibernate session with entiy-mode as map.
you can use a map to store and retuive information from db. Remember, since you are using map there will be difficulties in 2-way mapping (this was as of 3.3, not sure if its been fixed in later releses)

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).

Categories

Resources