Persisting Serializable Objects in Hibernate - java

I am attempting to persist objects that contain some large Serializable types. I want Hibernate to automatically generate my DDL (using Hibernate annotations). For the most part, this works, but the default database column type used by Hibernate when persisting these types is tinyblob. Unfortunately, this causes crashes when attempting to persist my class, because these types will not fit within the length of tinyblob.
However, if I manually set the type (using #Column(columnDefinition="longblob"), or more portably #Column(length=500000)), it works fine. Is there any way to make the default binary type longblob instead of tinyblob, so that I don't need to manually specify the #Column annotation on each field?
ExampleClass.java:
public class ExampleClass
{
#Column(columnDefinition="longblob")
ExampleSerializable ser1;
#Column(columnDefinition="longblob")
ExampleSerializable ser2;
...
}
ExampleSerializable.java:
public class ExampleSerializable implements java.io.Serializable
{
// MANY Fields
}
EDIT
Since there seems to be some confusion: annotating each field with #Column(columnDefinition="longblob") (or more portably: #Column(length=500000)), already works. I am looking for a solution that does not require me to annotate each and every field.

I think (didn't test it) that Hibernate will generate a tinyblob, blob, mediumblob column depending on the column length (respectively 255, 65535, 16777215) which defaults to 255. I would try to specify it (and this would be a portable solution).
Now, if really you want to force things, you'll have to extend the MySQL dialect (but this will harm portability).

Related

Hibernate Embeddable: validate on post load

I have an #Embeddable class with two fields: type and value. Both fields map to the corresponding database columns. The first one is enum and the latter one is an interface that has multiple implementations.
Only certain combinations of type and value are considered valid even if type and value are correct in isolation. How can I perform such validation, when I retrieve the entity that owns the #Embeddable from the database?
I could perform validation inside no-args-constructor of embeddable, but as far as I'm concerned, Hibernate creates new #Embeddable instance with no-args-constructor and then injects the values with Java Reflection API. Therefore, if I access these two fields inside the constructor they will be null.
Is there an approach to register some PostLoad hook for the #Embeddable classes that Hibernate will trigger? I though about declaring PostLoad inside the entity itself and then calling MyEmbeddable.validate directly. But I think it's a dirty approach.
I added the class-level annotation to validate the whole object. It did work. Check out this question for more details.

Is it possible to map custom final classes in hibernate?

Suppose I have already made class which I wish to persist. I can't change it's code, i.e. can't put any annotations inside. Also, class is not following bean convention.
I.e. it is arbitrary complex class I wish to persist.
Is it possible to write some sort of custom serializer and deserializer (don't know how to name it) in Hibernate, so that I be able to read these classes as usual POJOs?
Hello the first question is can I map a "fina class" the answer to this question is YES as long as you dont use Hibernate Enchancing or some sort of instrumentation.
Now second question. Bean not following Bean Conventions. I guess this means no getters and setters. You can have Attribute level access so this is again not a problem.
Is it possible to write custom serializer in Hibernate. The answer here is NO. Why ? Because Hibernate is not about Serialization hibernate is about SQL. There is no strict requirement that a Hibernate Entity should be serialize-able.
Even though Hibernate does not enforce serialization. Can I still make my final class serialize-able even though it does not implement Serializable or Eternalizeable. Yes you need to wrap it into class implementing Serializable or Externalizeable and implement the doRead doWrite methods yourself.
Serialization to JSON or XML - this is not part of Hibernate neither is part of JPA. Serialization to these two formats is defined as part of the Jaxb and Jax-rs specifications.
Have a look at hibernate UserType and CompositeUserType, with the well known EnumUserType example
Enums are a bit like your needs : final class, no getters nor setters. They are not complex though, so you might need a CompositeUserType that allows to map several columns for one Type, rather that a UserType.
Then you would use it like that in your class :
public class MyClass {
#Id
private Long id;
#Type(type = "com...MyCompositeUserType")
private ComplexFinalClassNotPojo complexObject;
}

What does #Basic annotation really do?

It seems that #Basic annotation on a java variable only declares that the variable must be saved as a column with NOT NULL constraint. Is that correct ?
This post says that:
#Basic(optional = false) #Column(nullable = false) The #Basic
annotation marks the property as not optional on the Java object
level. The second setting, nullable = false on the column mapping, is
only responsible for the generation of a NOT NULL database constraint.
The Hibernate JPA implementation treats both options the same way in
any case, so you may as well use only one of the annotations for this
purpose.
I am confused. What does this mean - The #Basic annotation marks the property as not optional on the Java object level. How is a property or variable "optional" at Java level ?
The Hibernate JPA implementation will treat both the same only in terms of schema generation, that is the column will be created with a not null constraint.
Using optional = false however also allows for Hibernate (and I suppose other implementations) to perform a check and throw an exception prior to flushing to the database if the non-optional field is null. Without this you would only get an Exception thrown after the attempt to insert.
From Pro JPA:
When the optional element is specified as false, it indicates to the
provider that the field or property mapping may not be null. The API
does not actually define what the behavior is in the case when the
value is null, but the provider may choose to throw an exception or
simply do something else. For basic mappings, it is only a hint and
can be completely ignored. The optional element may also be used by
the provider when doing schema generation, because, if optional is set
to true, then the column in the database must also be nullable.
Having optional=false can also affect Entity loading in Hibernate. For example, single-ended associations are always eagerly loaded in Hibernate unless the association is marked as optional=false.
See: https://stackoverflow.com/a/17987718/1356423 for further explanation.
The authoritative answer to the meaning of an api element is of course the api documentation, i.e. the javadoc. For the #Basic annotation, this writes:
The simplest type of mapping to a database column. The Basic annotation can be applied to a persistent property or instance variable of any of the following types: Java primitive types, wrappers of the primitive types, String, java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp, byte[], Byte[], char[], Character[], enums, and any other type that implements java.io.Serializable.
The use of the Basic annotation is optional for persistent fields and properties of these types. If the Basic annotation is not specified for such a field or property, the default values of the Basic annotation will apply.
What are the values of the Basic annotation? The Javadoc explains them, too:
public abstract FetchType fetch
(Optional) Defines whether the value of the field or property should be lazily loaded or must be eagerly fetched. The EAGER strategy is a requirement on the persistence provider runtime that the value must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime. If not specified, defaults to EAGER.
and
public abstract boolean optional
(Optional) Defines whether the value of the field or property may be null. This is a hint and is disregarded for primitive types; it may be used in schema generation. If not specified, defaults to true.
Therefore, if you set optional to false, the persistence provider may throw an exception when you try to persist or update an object where the property is null. This can be useful if your business rules say that null is not a legal value.
Note
At least when using hibernate, nullability is better expressed with the corresponding Bean Validation annotation (#NotNull), as this annotation is both understood by hibernate and can be used by other layers on an application (for instance when validating user input).

The better solution to extend DTO object (Java, Hibernate)

I use EmailAlert bean as DTO to get data by means of Hibernate.
So, my class contains only fields that I have in DB.
But in some cases I need additional fields to be in EmailAlert to hold intermediate data. For example "caption" field - will be calculated on java side depends of user locale, time, etc.
So, I have some variants to solve this issue.
Add additional property (ex: caption) to EmailAlert bean, but do not map it with any field of DB table.
Drawback: In this case we have to do not use "caption" property in hashCode() and equals() because as:
It really don't have a matter - field holds only intermediate data
I am not sure it not be a cause of problem with cache and Hibernate itself.
I think it is very ugly to have a property of class but do not use it in equals() and hashCode() methods.
Someone can be confusing in the future with this logic.
Extend EmailAlert as EmailAlertExt with adding of "caption" property. And constructor that takes EmailAlert as argument.
But in this case I am not sure underwater stones in case I will store EmailAlert as EmailAlertExt bean again into DB.
Extend EmailAlert as EmailAlertExt2 with adding of "caption" property and take a refference to the original object. In this case EmailAlertExt2 will behave as original EmailAlert, but with additional property we need. In case we save EmailAlert we could call getOriginalValue() of EmailAlertExt2 that will return refference to original object.
Drawback: too many coding :)
Guys, which of these solutions is better? May be someone have other proposals?
Use '#Transient' it won't map to db hibernate will ignore this field
Extending a model object just because you want to separate mapped vs non-mapped fields is not a good idea. A good guideline would be to ask yourself the question "What is the difference between an EmailAlert and an EmailAlertX, and can I clearly define the situations where I would use one over the other?". If you cannot answer that question cleanly, or if you realize that you will always be using your subclass over the parent class, that is a sure sign that the parent class should be abstract or that you have too many classes.
In your particular case, it would make more sense to have both the mapped, and non-mapped properties on the same class, and to mark the non-mapped properties so that your ORM provider does not try to process them. You can do this by annotating these properties as being #Transient.
public class EmailAlert implements Serializable {
#Id
private Long id;
#Column(name = "recipient")
private String recipient;
#Transient
private transient String caption;
// Constructor, Getters/Setters, etc
}
Also, with respect to to your comment on hashcode/equals methods. You do not and should not include every property of a Java Bean in these methods. Only include those properties that are:
required to uniquely identify the object
are (fairly) guaranteed to have the same value over the lifecycle of the object
It sounds like the EmailAlert object you need at the moment is a business object, because of the "intermediate data" and "calculated on java side" bits.
Maybe use the EmailAlertDto object to populate the fields of the EmailAlertBusiness and store the extra caption field and the methods in the business object.

Using enum as id

Using JPA, can we define an enum as id of an entity?
I've tried the following:
public enum AssetType {
....
}
#Entity
#IdClass(AssetType.class)
public class Adkeys {
private AssetType type;
#Id
#Enumerated(EnumType.STRING)
#Column(nullable = false)
public AssetType getType() {
return type;
}
}
Using OpenJPA, it complains:
org.apache.openjpa.persistence.ArgumentException: The id class "class aa.AssetType" specified by type "class aa.Adkeys" does not have a public no-args constructor.
So my questions are:
should we able to use enum as id for an entity on JPA? (i.e. there is a bug in OpenJPA)
or do I make a mistake somewhere?
and is there any workaround for such problem?
The JPA spec doesn't say this is possible:
2.1.4 Primary Keys and Entity Identity
The primary key (or field or property of a composite primary key) should be one of the following types: any Java primitive type; any primitive wrapper type; java.lang.String; java.util.Date; java.sql.Date. In general, however, approximate numeric types (e.g., floating point types) should never be used in primary keys. Entities whose primary keys use types other than these will not be portable.
If you really want to have a compile-time fixed number of records for a given entity, you can use a String or int primary key and assign it AssetType.FOO.name() or AssetType.FOO.ordinal()
And non-portable here means that some persistence provider may support other things, but it might not work for another provider. As with the enum - if the persistence provider has special support for it, that does not try to instantiate it, but rather processes it specially after checking if class.isEnum(), then it might work. But it seems your persistence provider doesn't do this.
No, you can't use enums as ID because JPA doesn't allow to define your own mapping for ID columns (they must be int or long or something that JPA can create with new).
IDs must not be the business key (in your case: the type). Using the business key as an ID is a common mistake in DB designs and should be avoided because it will cause all kinds of problems later.
Add an independent ID column to solve the problem.
OpenJPA is the only JPA provider that does not support this.
See Support Enum as Primary Key Type
Do you really want to do this? This construct doesn't allow changing the database enum keys without updating the enum in the code (fail on load), nor the other way around (constraint failure). Why don't you just create an AssetType table with int pk and name, and make the Adkeys have a foreign key to AssetType.id as pk?
You can load the AssetTypes from the db on startup if you need to enumerate them in your app.

Categories

Resources