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;
}
Related
I'm implementing caching in my Spring Boot (v2.5.2) app using GemFire.
The class which needs to be cached -
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
import com.auth0.jwk.Jwk;
#Region("jwks")
public class Key {
#Id
private String id;
private Jwk jwk;
#PersistenceConstructor
public Key(String id, Jwk jwk){
this.id = id;
this.jwk = jwk;
}
// Getters and setters for variables
}
I get the following error while fetching the stored entity from cache -
org.apache.geode.SerializationException : While deserializing query result with root cause
java.lang.NoSuchMethodException: com.auth0.jwk.Jwk.<init>()
at java.lang.Class.getConstructors0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2187)
...
How can I deserialize this object when Jwk doesn't have a no-arg constructor?
Edit -
build.gradle
implementation 'org.springframework.geode:spring-geode-starter'
implementation 'org.springframework.geode:spring-data-geode'
Also, updated the constructor as per suggested answer which continues to give the same error.
Error causing code -
Optional<Key> key = this.keyRepository.findById(id);
First, please be precise when you ask questions.
Your Key class is not even valid Java. The Key class by itself would not even compile as is.
The constructors (JsonWebKey) in this case are 1) not named after the class (Key) and 2) you cannot have 2 no arg constructors.
I am assuming the second constructor would be:
public Key(String id, Jwk jwk) {
this.id = id;
this.jwk = jwk;
}
I am guessing either the constructors are misnamed or perhaps are part of some [static] inner class??
Never-the-less, assuming you are using Spring Boot for Apache Geode (and VMware Tanzu (Pivotal) GemFire), a.k.a. SBDG (see here; if you are not using SBDG, you should be!), and since SBDG enables GemFire/Geode PDX serialization by default, then SBDG with the help of Spring Data for Apache Geode (and VMware Tanzu GemFire), a.k.a. SDG (upon which SBDG is built, along with the core Spring Framework, Spring Data and Spring Boot) will handle these serialization concerns for you, with no extra effort on your part. See the corresponding SDG documentation.
By way of example, I wrote this test class, which resides in this package, to demonstrate.
By default, the test class is using PDX serialization configured with Spring using the test configuration. The Spring-based configuration class is here. The other test configuration classes are only enabled with the appropriate test Spring profile configured.
The model class for this test is the CompositeValue class in the model sub-package.
As you can see, the class has 2 primary constructors. I also declared a commented-out, default, public no-arg constructor, which I will explain further below.
This CompositeValue model class was designed very deliberately. You will notice that it is (mostly) immutable.
I use a Spring Data CrudRepository (see here) to save (persist/store) an instance of CompositeValue in a GemFire/Geode Region ("Values") and then retrieve it (findBy..). The "Values" Region is necessarily a PARTITION Region (see here), since a PARTITION Region stores values in serialized form, and in our case, PDX serialized.
Using the Spring configuration, the test runs and passes successfully! Spring is doing all the heavy lifting!
If you are NOT using Spring to its fullest extent, then you are (most likely) going to have problems unless you know what you are doing with GemFire/Geode.
Out-of-the-box, GemFire/Geode PDX serialization has certain limitations. See the documentation. Specifically, see here.
For instance, if you are using GemFire/Geode's ReflectionBasedAutoSerializer class (Javadoc, documentation; and I suspect you are) and not Spring, then it requires your application domain objects (model classes / entities) to have a default, public no-arg constructor.
This flies in the face of immutable, effectively immutable and mostly immutable classes since then you cannot appropriately initialize classes using constructors, which is crucial in a highly concurrent, multi-Threaded context, like GemFire/Geode.
You can see the effects of trying to use GemFire/Geode's ReflectionBasedAutoSerializer by enabling the "gemfire" profile in the example test class I wrote, for which the configuration is here.
The test will NOT pass without the commented-out, default, public no-arg constructor.
When using Apache Geode 1.13.4, I get the following error:
2021-11-01 11:53:08,720 WARN ode.pdx.internal.AutoSerializableManager: 274 - Class
io.stackoverflow.questions.spring.geode.serialization.pdx.model.CompositeValue
matched with '.*' cannot be auto-serialized due to missing public no-arg constructor.
Will attempt using Java serialization.
However, even with Java serialization (the GemFire/Geode backup serialization strategy), the test results in a failure:
Caused by: java.io.NotSerializableException: io.stackoverflow.questions.spring.geode.serialization.pdx.model.CompositeValue
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at org.apache.geode.internal.InternalDataSerializer.writeSerializableObject(InternalDataSerializer.java:2184)
at org.apache.geode.internal.InternalDataSerializer.basicWriteObject(InternalDataSerializer.java:2058)
at org.apache.geode.DataSerializer.writeObject(DataSerializer.java:2839)
at org.apache.geode.internal.cache.CachedDeserializableFactory.calcSerializedSize(CachedDeserializableFactory.java:245)
... 60 common frames omitted
Well, the java.io.NotSerializableException is thrown because the CompositeValue class (deliberately) does not implement the java.io.Serialiable interface.
Why deliberately? Because you cannot implement java.io.Serializable on classes you do not own, which is true when using 3rd party libraries and their classes. Even though we own the CompositeValue in my case, I am making a point, because in your case, you don't own Jwk.
So, not only can we not use (mostly/effectively) immutable classes, we also cannot rely on default serialization mechanisms baked into GemFire/Geode.
Of course, we can handle this by implementing a custom PdxSerializer, the second strategy in the documentation (under "Procedure", Step 1, "Serializing your Domain Object with a PdxSerializer").
If we again change the active Spring profile to "gemfire-custom-pdxserializer" in the example test I wrote, then the test will pass.
But, it comes at a high price! See the necessary configuration to make this arrangement work.
In our case, we have only 1 such model / entity class to build a custom PdxSerializer for. However, imagine if we had hundreds of classes to handle.
To make matters worse, GemFire/Geode only allows a single PdxSerializer to be registered with a Singleton GemFire/Geode cache, which means you can only have 1. Now you must rely on the Composite Software Design Pattern to compose multiple PdxSerializers necessary to handle all your application domain model types requiring serialization. While this is elegant, you must build a custom PdxSerializer per application model / entity type. Of course, you could bake all type handling into 1 PdxSerializer implementation, but that would get ugly rather quickly!
Finally, your application model / entity types could implement GemFire/Geode's PdxSerializable interface (Javadoc). This is no better than java.io.Serializable and (again) does not work for types you don't own. It also couples your application to GemFire/Geode and is why I did NOT demonstrate this approach, as it should be considered an anti-pattern.
With SDG's MappingPdxSerializer, which SBDG's auto-configures for you (by default), you do not need to do any of the above. SBDG auto-configures PDX by default (making the SDG #EnablePdx annotation unnecessary) and there is no special requirement for your application domain object (model) / entity classes.
However, if you have more than 1 constructor in your entity class, then you will need to designate 1 constructor as the primary persistence constructor. In the CompositeValue class, this constructor was designated as the primary, persistence constructor using Spring Data's #PersistenceConstructor annotation, which SDG's MappingPdxSerializer takes into account when deserializing and constructing your application domain object model types.
If you only have 1 constructor in your class, then you do not even need to declare the #PersistenceConstructor annotation on the only constructor. That is if the other constructor in CompositeValue did not exist, then the #PersistenceConstructor annotation on this constructor would not be necessary. SD[G] can figure it out.
Feel free to play around with my example test for you learning purposes.
We use JSON serialization with Jackson to expose internal state of the system for debugging properties.
By default jackson does not serialize transient fields - but I wish to serialize them as well.
How can I serialize these fields?
One way I know is to supply a getters for these fields - but I don't want to do that, as I have some getX methods that I don't want to be invoked ( for instance, there are some getters that change the objects state ).
I know I could create an annotation, but I really want to avoid it.
So my question is:
Is there a way to setup jackson to serialize all the objects fields? include transient ones.
My solution with Jackson 2.4.3:
private static final ObjectMapper mapper =
new ObjectMapper(){{
Hibernate4Module module = new Hibernate4Module();
module.disable(Hibernate4Module.Feature.USE_TRANSIENT_ANNOTATION);
registerModule(module);
}};
I don't think Jackson supports any type of configuration to enable it to serialize a transient field. There's an open issue to add that feature, but it's old and hasn't been addressed (as far as I can tell): http://jira.codehaus.org/browse/JACKSON-623
So my question is: Is there a way to setup jackson to serialize all
the objects fields? include transient ones.
So to answer your question, no.
Some other Java JSON tools, such as GSON do support a configuration option to serialize transient fields. If you can use another tool, you might look into that (for GSON, see: https://sites.google.com/site/gson/gson-user-guide).
To expand a little, you might try a different approach.
First, You shouldn't try to serialize a transient field. After all the definition of transient is "don't serialize this." Nevertheless I can think of a few specific situations where it might be necessary, or at least convenient (like when working with code you can't modify or such). Still, in 99% of cases, the answer is don't do that. Change the field so that it's not transient if you need to serialize it. If you have multiple contexts where you use the same field, and you want it serialized in one (JSON, for example), and not serialized in another (java.io, for example) then you should create a custom serializer for the case where you don't want it, rather than abuse the keyword.
Second, as to using a getter and having "some getters that change the objects state," you should try to avoid that too. That can lead to various unintended consequences. And, technically, that's not a getter, that's a setter. What I mean is, if it mutates state, you've got a mutator (setter) rather than accessor (getter), even if you name it following the "get" convention and return some stuff.
You can create a custom getter for that transient field and use #XmlElement attribute. It doesn´t matter the name of that getter.
For example:
public class Person {
#XmlTransient private String lastname;
#XmlElement(name="lastname")
public String getAnyNameOfMethod(){
return lastname;
}
}
Another way to let Jackson serialize property is to add #JsonProperty annotation above it.
I guess it's better approach cause you do not need to disable default behaviour for all #Transient fields, like in Gere's answer.
I have some boolean fields in my JPA entities which are annotated in the following way:
#Column(length = 1)
#Type(type = "yes_no")
private final boolean myField;
Is it possible to create a combo-annotation (e. g. MyAnnotation) which combines both of this annotations?
#MyAnnotation
private final boolean myField;
How?
Obviously you could create an annotation that provides the equivalent of multiple other annotations.
Equally obviously no JPA provider will support it, since they will check for the javax.persistence annotations only (not that #Type is javax.persistence).
What you want is similar to CDI stereotypes - unfortunately, JPA does not support such a concept, therefore you must copy recurring annotations all over.
If you can afford to wrap your field into an object, you may mark it as #Embeddable and put your field into it - the annotations on that field will be copied wherever you embed that object into an entity. You may extend the annotations using #AnnotationOverrides. Obviously, the drawback of such solution is additional complexity when accessing the field in your entity.
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.
How can I combine annotations in Java?
EDIT I was asking if I two annotations a and b, can I combine to a single annotations c?
If possible, how do I do that?
You cannot combine the annotations by e.g. annotating the annotations, unless the annotation consumer will process the meta-annotation tree explicitly. For example, Spring supports such feature for #Transactional, #Component and some other annotations (you may wish to have a look at SpringTransactionAnnotationParser#parseTransactionAnnotation()). Nice to have this feature in Java core, but alas...
However you can declare the common parent class that has a set of annotations you need and extend it. But this is not always applicable.
Assuming you want to have multiple annotations on a single element, you can just list them in sequence.
The Wikipedia page on Java annotations has quite a few useful examples, e.g.
#Entity // Declares this an entity bean
#Table(name = "people") // Maps the bean to SQL table "people"
class Person implements Serializable {
...
}