Is it possible to store something like the following using only one table? Right now, what hibernate will do is create two tables, one for Families and one for people. I would like for the familymembers object to be serialized into the column in the database.
#Entity(name = "family")
class Family{
private final List<Person> familyMembers;
}
class Person{
String firstName, lastName;
int age;
}
This is an horrible design and I'm really not recommending it (you should just create another table) but it is possible.
First, you'll need to use a byte[] attribute to hold a serialized version of the list of persons that will be stored in a BLOB in the database. So annotate it's getter with #Lob (I would make the getter and setter private to not expose them). Then, expose "fake" getter and setter to return or set a List<Person> from the byte[]. I'm using SerializationUtils from Commons Lang in the sample below (provide you own helper class if you don't want to import this library) to serialize/deserialize on the fly to/from the byte[]. Don't forget to mark the "fake" getter with #Transcient or Hibernate will try to create a field (and fail because it won't be able to determine the type for a List).
#Entity(name = "family")
class Family implements Serializable {
// ...
private byte[] familyMembersAsByteArray;
public Family() {}
#Lob
#Column(name = "members", length = Integer.MAX_VALUE - 1)
private byte[] getFamilyMembersAsByteArray() { // not exposed
return familyMembersAsByteArray;
}
private void setFamilyMembersAsByteArray((byte[] familyMembersAsByteArray() { // not exposed
this.familyMembersAsByteArray = familyMembersAsByteArray;
}
#Transient
public List<Person> getFamilyMembers() {
return (List<Person>) SerializationUtils.deserialize(familyMembersAsByteArray);
}
public void setParticipants(List familyMembers) {
this.familyMembersAsByteArray = SerializationUtils.serialize((Serializable) familyMembers);
}
}
Don't forget to make the Person class Serializable and to add a real serialVersionUID (I'm just showing a default here):
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
// ...
private String firstName, lastName;
private int age;
}
But, let me insist, this is an horrible design and it will be very fragile (changing Person might require to "migrate" the content of the BLOB to avoid deserialization issues and this will become painful. You should really reconsider this idea and use another table for the Person instead (or I don't get why you use a database).
#Type(type = "serializable")
private List<Person> familyMembers;
if you can't use hibernate annotations try this:
#Lob
private Serializable familyMembers;
public List<Person> getFamilyMembers(){
return (List) familyMembers;
}
public void setFamilyMembers(List<Person> family){
familyMembers = family;
}
Annotate the property with #Column and define the type to be ArrayList, not just List. And make Person implement Serializable.
But you should do this only if your motives are very clear, because this is the correct solution in some very rare cases. As Pascal noted, if you ever have to change Person you'll have headaches.
You can create pseudoproperty (getter and setter) which accepts/returns the serialized form, and annotate the familyMembers with #Transient. This would also need to annotate the getters, not fields, for all other properties.
Related
Mongodb is a no-schema document database, but in spring data, it's necessary to define entity class and repository class, like following:
Entity class:
#Document(collection = "users")
public class User implements UserDetails {
#Id private String userId;
#NotNull #Indexed(unique = true) private String username;
#NotNull private String password;
#NotNull private String name;
#NotNull private String email;
}
Repository class:
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String username);
}
Is there anyway to use map not class in spring data mongodb so that the server can accept any dynamic JSON data then store it in BSON without any pre-class define?
First, a few insightful links about schemaless data:
what does “schemaless” even mean anyway?
“schemaless” doesn't mean “schemafree”
Second... one may wonder if Spring, or Java, is the right solution for your problem - why not a more dynamic tool, such a Ruby, Python or the Mongoshell?
That being said, let's focus on the technical issue.
If your goal is only to store random data, you could basically just define your own controller and use the MongoDB Java Driver directly.
If you really insist on having no predefined schema for your domain object class, use this:
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
private Map<String, Object> schemalessData;
// getters/setters omitted
}
Basically it gives you a container in which you can put whatever you want, but watch out for serialization/deserialization issues (this may become tricky if you had ObjectIds and DBRefs in your nested document). Also, updating data may become nasty if your data hierarchy becomes too complex.
Still, at some point, you'll realize your data indeed has a schema that can be pinpointed and put into well-defined POJOs.
Update
A late update since people still happen to read this post in 2020: the Jackson annotations JsonAnyGetter and JsonAnySetter let you hide the root of the schemaless-data container so your unknown fields can be sent as top-level fields in your payload. They will still be stored nested in your MongoDB document, but will appear as top-level fields when the ressource is requested through Spring.
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
// add all other expected fields (getters/setters omitted)
private String foo;
private String bar;
// a container for all unexpected fields
private Map<String, Object> schemalessData;
#JsonAnySetter
public void add(String key, Object value) {
if (null == schemalessData) {
schemalessData = new HashMap<>();
}
schemalessData.put(key, value);
}
#JsonAnyGetter
public Map<String, Object> get() {
return schemalessData;
}
// getters/setters omitted
}
I am building an application which allows restaurant guests to order food and send to server.
What i have considered is to
1) create a class Order.java class
public class Order
{
private Intger tableId;
private Integer restaurantId;
private Integer foodId;
private Integer foodQuantity;
getter and setters
}
2) This class object will be populated with guests order and an ArrayList of objects of the class will be sent to server as Gson String.
Now if an order consist of some 7 items, Then the arraylist will have 7 objects, but tableId and restaurantId will be same 7 times.
can you suggest a better design where in I can associate restaurantId and tableId with the entire arraylist.
Thanks.
There is no right solution, it would depend on your needs, one possible solution would be something like:
public class Order {
private int tableId;
private int restaurant;
private List<OrderItem> items;
// setters and getters
}
public class OrderItem {
private int itemId; // foodId
private int quatity; // foodQuantity
// setters and getters
}
But if you were in a situation that the information comes not normalized, like you suggested (in which tableId is repeated for every single food ordered), I would consider to implement a normalization process that will return a structure with the classes I draft above. But if you are implementing it, please consider to make it as normalized as possible.
A class exists that I have no control over:
public class MyPerson {
private String name;
private int elevation;
// getters and setters
}
I want to persist this person into Mongo, but I cannot alter this class definition with #Entity and other annotations.
I'd like the document in Mongo to look something akin to:
{ name : "You", elevation : 65 }
What's the best way to approach this? Converter? Extended class? Containing class?
The easiest way would be to just embed it in one you control, and can put the #Id on.
Must work:
morhpia.map(MyPerson.class);
Datastore ds = morhpia.createDatastore(mongo, "my_database");
MyPerson pe = new MyPerson();
pe.setName("cmonkey");
ds.save(pe);
BUT
You must add #Id field in the POJO. Its is required by Morphia.
I am trying to use an inner class as embeddable to represent some complicated properties of the outer class. When I store this, there is no information from the inner class in the database schema generated by eclipselink.
Does what I'm trying to do seem like a good idea? Why doesn't eclipselink seem to recognize them #Basic attribute on the getRate() in Attributes?
Some other info: Measure must be instantiated using a factory which is provided to the constructor of Person, so I don't even know how I'm going to be able to use this at all. It seems more and more likely that I'll have to make a separate class just to store the state of Person in simple terms (like doubles, not Measures) and use those to create the real Person-type objects, but that has very sad implications for the rest of my application.
#Entity
public static class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Transient
public Measure<Double, CleanupRate> rate;
#Embedded
private Attributes attributes;
#Embeddable
public static class Attributes {
#Transient
private Person person;
public Attributes() {
}
public Attributes(Person person) {
this.person = person;
}
#Basic
public double getRate() {
return person.rate.getInternalValue();
}
public void setRate(double value) {
person.rate.setInternalValue(value);
}
}
public Person() {
rate = udm.getMeasureFactory().createMeasure(0.0, CleanupRate.class);
attributes = new Attributes(this);
}
public void setRate(double rate) {
this.rate.setValue(rate);
}
}
Edit:
In order to inject the measure dependency into my objects when they are retrieved from storage, I've added an interface which injects the dependency and used it in my DAO. Since the DAO can be injected, I can propagate the dependency down to the retrieved objects. I got the idea from a blog.
private <T extends UomInjectable> List<T> //
getListOfUomInjectableType(final Class<T> klass) {
List<T> result = getListOfType(klass);
for (UomInjectable injectable : result) {
injectable.injectUomFactory(udm);
}
return result;
}
It is using the access type from the Person class, which is set to field, and so not seeing the annotation at the property level.
You will need to change the access type using Access(PROPERTY) on the embeddable class, and should remove the #Transient annotation on the person attribute.
I think in general you're going to be in trouble having Entities (Embeddable or otherwise) that need constructors with arguments. I'm not sure how that might be related to your schema generation issue, but I think this will be a problem trying to persist/retrieve these objects.
As you hinted, JPA requires all entity types to have a no-argument constructor. While your Attributes class has one, it leaves the 'person' field as null which will fairly quickly result in NPE's. Same with the Person constructor (maybe you left out the one that passes in 'udm' from the sample code?).
The set the Person for the Attributes, just use property access in Person and set it in your setAttributes method.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Embeddables#Relationships
I am trying to model such situation - there is a cash transfer (I mean a car that carries money), that has required amounts of each currency, and also an actual amount for each currency. And it seems to me pointless to create two separate classes, one for required amount and another for actual amount. So the implementation would look like this:
#Entity
public class CashTransferCurrencyAmount {
// id, version and so on
#Column(length = 3)
private String currencyCode;
#Basic
private BigDecimal amount;
#ManyToOne
private CashTransfer cashTransfer;
}
#Entity
public class CashTransfer {
// id, version and so on
#OneToMany(mappedBy="cashTransfer")
private Set<CashTransferCurrencyAmount> requiredCurrencyAmountSet = new HashSet<CashTransferAmountCurrency>();
#OneToMany(mappedBy="cashTransfer")
private Set<CashTransferCurrencyAmount> actualCurrencyAmountSet = new HashSet<CashTransferAmountCurrency>();
}
But how is a CashTransferCurrencyAmount instance to know to which collection it belongs? I have two ideas:
1 - add a discriminator field to CashTransferCurrencyAmount:
public enum RequestType {
ACTUAL,
REQUIRED
}
#Basic
#Enumerated(EnumType.STRING)
private RequestType requestType;
and add #WHERE annotations to collections in CashTransfer. This is preferable for me.
2 - create two join tables. one for mapping requested amounts and one for mapping actual amounts. I dislike this one as I don't want too many tables in my DB.
Are there any other ways to achieve this? I this approach correct?
And please don't tell me to put both requested and actual amounts in one entity. The real case is more complicated, each CashTransferCurrencyAmount has it's own collections so it can't be solved that way.
EDIT
As for requests for complete story - there used to be two values in CashTransferCurrencyAmount - required (I think it should be 'requested') and actual, but now each amount has it's own collection - how this amount is split into denominations. So I need a collection of amounts, each one having a collection of denominations. The type of CurrencyAmount and CurencyDenomination seems to be the same for requested ones and for actual ones.
Since you want CashTransferCurrencyAmount instance to know which collection it belongs to, I assume you want to have some logic based on that. The way I would model your situation would be using inheritance.
You're saying "it seems to me pointless to create two separate classes", I would however try to convince you that you should. You could use a "Single Table" inheritance type, so that you don't introduce additional tables in your DB, which is what you're trying to accomplish.
My shot would look something like:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "request_type", discriminatorType = DiscriminatorType.STRING)
public abstract class CashTransferCurrencyAmount {
// id, version and so on
#Column(length = 3)
private String currencyCode;
#Basic
private BigDecimal amount;
#ManyToOne
private CashTransfer cashTransfer;
}
#Entity
#DiscriminatorValue("REQUIRED")
public class CashTransferCurrencyAmountRequired extends CashTransferCurrencyAmount {
// required anount specific stuff here
}
#Entity
#DiscriminatorValue("ACTUAL")
public class CashTransferCurrencyAmountActual extends CashTransferCurrencyAmount {
// actual anount specific stuff here
}
#Entity
public class CashTransfer {
// id, version and so on
#OneToMany(mappedBy="cashTransfer")
private Set requiredCurrencyAmountSet = new HashSet();
//Stackoverflow deleting my generic sets! But it's exactly the same as in your code...
#OneToMany(mappedBy="cashTransfer")
private Set actualCurrencyAmountSet = new HashSet();
}