Eclipselink: Adding Where-Clause using customizer - java

In my current project, we will refractor the nls-support for database entities. In our previous version, we used the session language, but unfortualy this behaviour was not completely stable. So we will change the code, so that the language information is stored inside the query.
I would love to have a central instance to handle this language behaviour instead of changing each query, in every entity spread over the whole project.
E.g. I have this entity:
#NamedQueries({
#NamedQuery(name = NLSBackendEntity.findAll,
query = "select n from NLSBackendEntity n"),
#NamedQuery(name = NLSBackendEntity.getById,
query = "select n from NLSBackendEntity n where n.nlsBackendKey.key = :key") })
#Entity
#Table(name = "backend_key_al")
public class NLSBackendEntity implements Serializable
{
private static final long serialVersionUID = 1L;
public static final String findAll = "NLSBackend.findAll";
public static final String getById = "NLSBackend.getById";
#EmbeddedId
private NLSBackendKey nlsBackendKey;
/**
* The text in the language.
*/
#Lob
#Column(name = "TEXT")
private String text;
NLSBackendEntity()
{
// no arg constructor needed for JPA
}
public String getKey()
{
return nlsBackendKey.key;
}
public String getLanguage()
{
return nlsBackendKey.language;
}
public String getText()
{
return text;
}
#Embeddable
public static class NLSBackendKey implements Serializable
{
private static final long serialVersionUID = 1L;
/**
* the NLS-key.
*/
#Column(name = "KEY")
private String key;
/**
* The language of this entry.
*/
#Column(name = "LOCALE")
private String language;
}
}
One possibility would now be to add the n.nlsBackenKey.language = :locale to every NamedQuery and to change every call, where this NamedQuery is referenced.
A more favorable way would be, to have a Customizer to add the locale paramter. Atm I have this:
public class QueryLanguageCustomizer implements DescriptorCustomizer
{
#Override
public void customize(ClassDescriptor descriptor) throws Exception
{
ExpressionBuilder eb = new ExpressionBuilder(descriptor.getJavaClass());
Expression languageExp = eb.getField("LOCALE").equal(eb.getParameter("locale"));
descriptor.getQueryManager().setAdditionalJoinExpression(languageExp);
}
}
And I added this to the NLSBackendEntity: #Customizer(QueryLanguageCustomizer.class)
But now, I'm not able to set this parameter. Again, my favored way, would be to use a SessionEventAdapter:
public class LanguageSessionEventListener extends SessionEventAdapter {
/** Log for logging. */
private static final Log LOG = LogFactory
.getLog(LanguageSessionEventListener.class);
#Override
public void preExecuteQuery(SessionEvent event) {
LOG.debug("preExecuteQuery called for session= "
+ event.getSession().getName());
event.getQuery().getTranslationRow().put("LOCALE", getLocale());
super.preExecuteQuery(event);
}
private String getLocale() {
// uninteresting for this example
}
}
Unfortunatly no matter what I try, I'm unable to set this parameter. The getTransaltionRow() returns null, and every other possibility I tried failed.
Are there no possibilitys to set this parameter inside the preExecuteQuery block?
I'm using Eclipselink 2.5, any help is highly appreciated

If you don't mind using vendor-specific solution you could use EclipseLink #AdditionalCriteria annotation. You could use it as follows:
Create an abstract mapped class and derive all entities from it:
#MappedSuperclass
#AdditionalCriteria("this.language = :lang")
public class AbstractEntity {
private String language;
// getters + setters
}
Let your entities subclass it:
public class NLSBackendEntity extends AbstractEntity implements Serializable {
// ...
}
Set the value of language property on either the entity manager or entity manager factory:
entityManager.setProperty("language", "de");
before the queries are executed. EclipseLink should append language = ? to the where condition of your query binding the value you set on the entity manager.

You can use ThreadLocal<String> threadProps = new ThreadLocal<String>(); which you can set it on data or rest factory class and use it in your code.
The above solution will work if you not creating any new additional thread inside your functions.
Instead of parameter you can use threadProps.get();

Related

Is it possible to map a JPA/Hibernate Entity field to a field in another class without #Embeddable?

I have a very simple Entity (Person.java) that I am wanting to persist via JPA/Hibernate.
The Entity contains two fields: ID and Identification String.
The ID is a simple Integer, and is no problem. The Identification String is currently a String, but for various reasons, I want to instead use a wrapper class for String (IDString), where there are various validation methods among other things.
I am wondering how I can get JPA/Hibernate to use the wrapped string (inside the custom class IDString) when persisting the Person table in the database. I know this can probably be solved by letting the IDString be #Embeddable and then embed IDString in the Person entity with #Embedded, but I am looking for another method, mostly because IDString is in an entirely different package, and I am reluctant to have to go there and change stuff.
Googling, I found https://www.baeldung.com/hibernate-custom-types, but it seems to be mostly about more complicated cases, where you want to convert one class into another type, and I do feel that there is probably a smarter way that I am simply overlooking.
Here is the entity (in theory)
#Entity(name="Person")
#Table(name="DB_TABLE_PERSON")
public class Person implements Serializable {
#Id
Integer id;
// WHAT SHOULD I PUT HERE? I WANT TO SIMPLY USE THE STRING INSIDE IDSTRING AS THE FIELD TO PERSIST
IDString idString;
// getter and setter for ID.
public void getIdString() {
return idString.getValue();
}
public void setIdString(String in) {
idString.setValue(in);
}
}
And here is the class IDString (in theory):
public class IDString {
// I really want to be a POJO
private final String the_string;
public IdString(String input) {
if (isValid(input)) {
the_string = input;
} else {
throw new SomeCoolException("Invalid format of the ID String");
}
public boolean isValid(String input) {
// bunch of code to validate the input string
}
public String getValue() {
return the_string;
}
public void setValue(String input) {
if (isValid(input)) the_string = s;
else throw new SomeCoolException("Invalid format of the ID String");
}
I know that I could place the validation if the IDString inside the Entity, but the IDString will be used elsewhere (it's a general custom class), so I don't want to do that. Is there a simple way?
#Converter(autoApply=true) // autoApply is reasonable, if not use #Converter on field
public class IDStringConverter implements AttributeConverter<IDString,String> {
#Override
public String convertToDatabaseColumn(IDString attribute) {
return attribute != null ? attribute.getValue() : null;
}
#Override
public IDString convertToEntityAttribute(String dbData) {
return dbData != null ? new IDString(dbData) : null;
}
}
With this you should not need any other modifications in your code. One limitation of the AttributeConverter is that it maps from exactly 1 Java field to exactly 1 DB column. If you wanted to map to more columns (not the case here), you would need embeddables.
You could also put a #Column annotation on the getter:
#Entity
public class Person {
private final IdString idString = new IdString();
#Column(name = "ID_STRiNG")
public IdString getIdString() {
return idString.getValue();
}
public void setIdString(String input) {
idString.setValue(input);
}
Another solution could be to convert to/from IdString using #PostLoad and #PrePersit event handlers:
#Entity
public class Person {
#Column(name = "ID_STRiNG")
private String the_string; // no getters & setters
#Transient
private final IdString idString = new IdString();
#PostLoad
public void postLoad() {
idString.setValue(the_string);
}
#PrePersist
public void prePersist() {
the_string = idString.getValue();
}
// getters & setters for idString

SDN6 - Projection interfaces with Property Mapping

I am using spring data neo4j 6.1.3 and following is my use case code snippets
Domain Entity
#Data
#Node("DATspace")
public class DatSpace {
#Id #GeneratedValue
private Long neoId;
#Property("SUPtitle")
private String title;
private String SUPid;
}
Test class
#SpringBootTest
#EnableNeo4jRepositories(basePackages = "com.rahal.marvel")
public class ProjectionTest {
#Autowired
private Neo4jTemplate neo4jTemplate;
interface DATspaceProjection {
String getTitle();
String getSUPid();
}
#Test
public void test_projection(){
DatSpace d = neo4jTemplate.findOne("MATCH (s:DATspace {SUPid: $id}) RETURN s", Collections.singletonMap("id", "SPC_ML7"), DatSpace.class).get();
d.setTitle("title modified");
d.setSUPid("SUPid modified");
DATspaceProjection p = neo4jTemplate.saveAs(d, DATspaceProjection.class);
}
}
Ideally above saveAs function should modify both DATspace.SUPtitle and DATspace.SUPid. However it only modify SUPid but not SUPtitle. I presume it is due to property mapping (#Property) . Is this a bug or are there any workaround?
The provided #Property annotation does only have an impact on the annotated property (title) itself.
There is no knowledge right now that goes from the getTitle() method in the projection to the annotated title field in the domain class.
To be safe when modifying this use the explicit property name:
interface DATspaceProjection {
String getSUPtitle();
String getSUPid();
}
I created an issue for improvement https://github.com/spring-projects/spring-data-neo4j/issues/2371

How to fix " Failed to instantiate 'className' using constructor NO_CONSTRUCTOR with arguments" in immutable class

I use MongoDBRepository in spring boot, and when I save some object in database everything is ok. but when I find object by id spring does not allow do that.
I try to change VehicleRoutingProblemSolution type to Object type, but VehicleRoutingProblemSolution have other object field PickupService and it without default constructor to. And yes, this class has immutable... I can't create default constructors, what can I do?
import com.fasterxml.jackson.annotation.JsonProperty;
import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "vrp_solutions")
public class VrpSolutionHolder {
// Specifies the solution id
#Id
#JsonProperty("id")
private String id;
// Specifies the solution id
#JsonProperty("solution")
private VehicleRoutingProblemSolution vehicleRoutingProblemSolution;
// Created at timestamp in millis
#JsonProperty("created_at")
private Long created_at = System.currentTimeMillis();
public VrpSolutionHolder(String id, VehicleRoutingProblemSolution vehicleRoutingProblemSolution) {
this.id = id;
this.vehicleRoutingProblemSolution = vehicleRoutingProblemSolution;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public VehicleRoutingProblemSolution getVehicleRoutingProblemSolution() {
return vehicleRoutingProblemSolution;
}
public void setVehicleRoutingProblemSolution(VehicleRoutingProblemSolution vehicleRoutingProblemSolution) {
this.vehicleRoutingProblemSolution = vehicleRoutingProblemSolution;
}
public Long getCreated_at() {
return created_at;
}
public void setCreated_at(Long created_at) {
this.created_at = created_at;
}
}
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
org.springframework.data.mapping.model.MappingInstantiationException:
Failed to instantiate
com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution
using constructor NO_CONSTRUCTOR with arguments
I ran into the exact same problem. A persistent immutable class containing other class instances, throwing that aforementioned exception when retrieved by this repository method:
public interface ProjectCodeCacheRepository extends MongoRepository<CachedCode, String> {
public CachedCode findByCode(String code);
public List<CachedCode> findByClientId(UUID clientId);
}
...
List<CachedCode> cachedForClient = this.codeCacheRepo.`**findByClientId**`(clientId);
...
Following Erwin Smouts hints, this is nicely fixed by giving it a special constructor annotated org.springframework.data.annotation.PersistenceConstructor like so:
#Document(collection="cachedcodes")
public class CachedCode {
#PersistenceConstructor
public CachedCode(String code, UUID clientId, LocalDateTime expiration) {
this.code = code;
this.clientId = clientId;
this.expiration = expiration;
}
public CachedCode(String code, UUID clientId, long secondsExpiring) {
this.code = code;
this.clientId = clientId;
this.expiration = LocalDateTime.now().plusSeconds(secondsExpiring);
}
public UUID getClientId( ) {
return this.clientId;
}
public String getCode() {
return this.code;
}
public boolean hasExpired(LocalDateTime now) {
return (expiration.isBefore(now));
}
...
#Id
private final String code;
private final UUID clientId;
private final LocalDateTime expiration;
}
So, you should check if your VehicleRoutingProblemSolution has a) a constructor that matches the database fields (check in mongo client) and b) is annotated to be the one used by the driver (or whichever piece of Spring magic under the hood).
If your framework tool requires (visible) no-arg constructors (plus accompanying setters), and the class you have is required to stay as is, then you could roll your own, say, MutableVehicleRoutingProblemSolution where in the setters you could have :
this.vehicleRoutingProblemSolution = new VehicleRoutingProblemSolution(vehicleRoutingProblemSolution.getId(), newSolution);
Thus your MutableVehicleRoutingProblemSolution wraps around the existing VehicleRoutingProblemSolution.
Hacky smell to it, but it fits the requirements.
(Or you could try to find a tool that is able to use, not annotations on the contained fields, but annotations on constructor arguments.)
This is a problem where the corresponding class does not have a no-arg constructor like - I was facing an issue with java.io.File.
Solution:
In general - change the declaration to Object class and convert where we are using the class.
from
class MyClass{
File myfile;
}
to
class MyClass{
Object myFile;
}
For anyone using lombok, you need to remove the #Builder annotation on your class and use #Data instead, or follow the above solution to provide a specialized constructor
Oddly, I received this when I attempted to decorate a custom interface with ...
#Document(collection = "Person")
Example:
package test.barry.interfaces;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
#Document(collection = "Person")
public interface CustomRepository
{
void updatex(Query filterPredicate, UpdateDefinition updatePredicate);
}

How to exclude property from Lombok builder?

I have a class called as "XYZClientWrapper" , which have following structure:
#Builder
XYZClientWrapper{
String name;
String domain;
XYZClient client;
}
What I want no build function generated for property XYZClient client
Does Lombok supports such use case?
Yes, you can place #Builder on a constructor or static (factory) method, containing just the fields you want.
Disclosure: I am a Lombok developer.
Alternatively, I found out that marking a field as final, static or static final instructs #Builder to ignore this field.
#Builder
public class MyClass {
private String myField;
private final String excludeThisField = "bar";
}
Lombok 1.16.10
Create the builder in code and add a private setter for your property.
#Builder
XYZClientWrapper{
String name;
String domain;
XYZClient client;
public static class XYZClientWrapperBuilder {
private XYZClientWrapperBuilder client(XYZClient client) { return this; }
}
}
Here is my preferred solution. With that, you can create your field client at the end and have it depending on other fields that previously set by the builder.
XYZClientWrapper{
String name;
String domain;
XYZClient client;
#Builder
public XYZClientWrapper(String name, String domain) {
this.name = name;
this.domain = domain;
this.client = calculateClient();
}
}
For factory static method example
class Car {
private String name;
private String model;
private Engine engine; // we want to ignore setting this
#Builder
private static Car of(String name, String model){
Car car=new Car();
car.name = name;
car.model = model;
constructEngine(car); // some static private method to construct engine internally
return car;
}
private static void constructEngine(Car car) {
// car.engine = blabla...
// construct engine internally
}
}
then you can use as follows:
Car toyotaCorollaCar=Car.builder().name("Toyota").model("Corolla").build();
// You can see now that Car.builder().engine() is not available
Notice the static method of will be called whenever build() is called, so doing something like Car.builder().name("Toyota") won't actually set the value "Toyota" into name unless build() is called and then assigning logic within the constructor static method of is executed.
Also, Notice that the of method is privately accessed so that build method is the only method visible to the callers
I found that I was able to implement a "shell" of the static Builder class, add the method I want to hide with a private access modifier, and it is no longer accessible in the builder. Likewise I can add custom methods to the builder as well.
package com.something;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import java.time.ZonedDateTime;
#Data
#Entity
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class MyClass{
//The builder will generate a method for this property for us.
private String anotherProperty;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "localDateTime", column = #Column(name = "some_date_local_date_time")),
#AttributeOverride(name = "zoneId", column = #Column(name = "some__date_zone_id"))
})
#Getter(AccessLevel.PRIVATE)
#Setter(AccessLevel.PRIVATE)
private ZonedDateTimeEmbeddable someDateInternal;
public ZonedDateTime getSomeDate() {
return someDateInternal.toZonedDateTime();
}
public void setSomeDate(ZonedDateTime someDate) {
someDateInternal = new ZonedDateTimeEmbeddable(someDate);
}
public static class MyClassBuilder {
//Prevent direct access to the internal private field by pre-creating builder method with private access.
private MyClassBuilder shipmentDateInternal(ZonedDateTimeEmbeddable zonedDateTimeEmbeddable) {
return this;
}
//Add a builder method because we don't have a field for this Type
public MyClassBuilder someDate(ZonedDateTime someDate) {
someDateInternal = new ZonedDateTimeEmbeddable(someDate);
return this;
}
}
}
I found one more solution
You can wrap your field into initiated final wrapper or proxy.
The easiest way to wrap it into AtomicReference.
#Builder
public class Example {
private String field1;
private String field2;
private final AtomicReference<String> excluded = new AtomicReference<>(null);
}
You can interact with it inside by get and set methods but it won't be appeared in builder.
excluded.set("Some value");
excluded.get();
Adding a so called 'partial builder' to the class with Lombok #Builder can help. The trick is to add a inner partial builder class like this:
#Getter
#Builder
class Human {
private final String name;
private final String surname;
private final Gender gender;
private final String prefix; // Should be hidden, depends on gender
// Partial builder to manage dependent fields, and hidden fields
public static class HumanBuilder {
public HumanBuilder gender(final Gender gender) {
this.gender = gender;
if (Gender.MALE == gender) {
this.prefix = "Mr.";
} else if (Gender.FEMALE == gender) {
this.prefix = "Ms.";
} else {
this.prefix = "";
}
return this;
}
// This method hides the field from external set
private HumanBuilder prefix(final String prefix) {
return this;
}
}
}
PS: #Builder allows the generated builder class name to be changed. The example above assumed the default builder class name is used.
I have another approach using #Delegate and Inner Class, which supports "computed values" for the excluded fields.
First, we move the fields to be excluded into an Inner Class to avoid Lombok from including them in the Builder.
Then, we use #Delegate to expose Getters/Setters of the builder-excluded fields.
Example:
#Builder
#Getter #Setter #ToString
class Person {
private String name;
private int value;
/* ... More builder-included fields here */
#Getter #Setter #ToString
private class BuilderIgnored {
private String position; // Not included in the Builder, and remain `null` until p.setPosition(...)
private String nickname; // Lazy initialized as `name+value`, but we can use setter to set a new value
/* ... More ignored fields here! ... */
public String getNickname(){ // Computed value for `nickname`
if(nickname == null){
nickname = name+value;
}
return nickname;
}
/* ... More computed fields' getters here! ... */
}
#Delegate #Getter(AccessLevel.NONE) // Delegate Lombok Getters/Setters and custom Getters
private final BuilderIgnored ignored = new BuilderIgnored();
}
It will be transparent to outside of this Person class that position and nickname are actually inner class' fields.
Person p = Person.builder().name("Test").value(123).build();
System.out.println(p); // Person(name=Test, value=123, ignored=Person.BuilderIgnored(position=null, nickname=Test123))
p.setNickname("Hello World");
p.setPosition("Manager");
System.out.println(p); // Person(name=Test, value=123, ignored=Person.BuilderIgnored(position=Manager, nickname=Hello World))
Pros:
Do not force the excluded fields to be final
Support computed values for the excluded fields
Allow computed fields to refer to any fields set by the builder (In other words, allow the inner class to be non-static class)
Do not need to repeat the list of all fields (Eg. listing all fields except the excluded ones in a constructor)
Do not override Lombok library's #Builder (Eg. creating MyBuilder extends FooBuilder)
Cons:
The excluded fields are actually fields of Inner Class; however, using private identifier with proper Getters/Setters you can mimic as if they were real fields
Therefore, this approach limits you to access the excluded fields using Getters/Setters
The computed values are lazy initialized when Getters are invoked, not when .build().
One method I like and use is this.
Keep required parameters in constructor, and set optional through builder. Works if number of required is not very big.
class A {
private int required1;
private int required2;
private int optional1;
private int optional2;
public A(int required1, int required2) {
this.required1 = required1;
this.required2 = required2;
}
#Builder(toBuilder = true)
public A setOptionals(int optional1, int optional2) {
this.optional1 = optional1;
this.optional2 = optional2;
return this;
}
}
And then construct it with
A a = new A(1, 2).builder().optional1(3).optional2(4).build();
Nice thing with this approach is that optionals can also have default value.
One approach I have used before was to group instance fields into Configuration fields and Session fields. Configuration fields go as class instances and are visible to the Builder, while Session fields go into a nested private static class and are accessed via a concrete final instance field (which the Builder will ignore by default).
Something like this:
#Builder
class XYZClientWrapper{
private String name;
private String domain;
private static class Session {
XYZClient client;
}
private final Session session = new Session();
private void initSession() {
session.client = ...;
}
public void foo() {
System.out.println("name: " + name);
System.out.println("domain: " + domain;
System.out.println("client: " + session.client);
}
}
To exclude field from builder, try using #Builder.Default

JPA eclipselink Inheritance between entities : oracle database

I'm facing a little problem with my web application which is in vaadin and i'm using jpa and eclipselink for the mapping. I have three entities :
encaiss (#MappedSuperclass contains just Id)
|
|
Encaissement (it contains the main and common properties)
/ \
/ \
Encaissement_Technique Encaissement_espece
When i create an entity "Encaissement" with "Espece" as type, it is well created in the table Encaissement but it doesn't exist in the table Encaissement_espece.
I guess that I should join the two tables according to the identifier (ID) which is in a #MappedSuperclass class. I would appreciate any help for managing my subordinate class (that is Encaissement_Technique and Encaissement_espece) because my next step would be to add records to those two tables from a simple form (so if i have a field "libelle" which is present in Encaissement but not in Encaissement_Espece how can make such instruction :
Encaissement_Espece espece= new Encaissement_Espece();
espece.setLibelle(field.getValue().toString());
Those are my entities :
encaiss, this class contain just the Id for all the classes
#MappedSuperclass
public abstract class encaiss {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="encaiss_seq_gen")
#SequenceGenerator(name="encaiss_seq_gen", sequenceName="ENCAISSEMENT_SEQ", allocationSize = 1, initialValue = 1)
protected Integer id_encaissement;
public Integer getId_encaissement() {
return id_encaissement;
}
public void setId_encaissement(Integer id_encaissement) {
this.id_encaissement = id_encaissement;
}
}
Encaissement (wich extend encaiss just to have an Id)
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="ENCAISS_TYPE")
#Table(name="ENCAISSEMENT")
public class Encaissement extends encaiss implements Serializable{
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_CLIENT")
private Client Client;
#Column(name="ENCAISS_TYPE")
protected String encaiss_type;
#Column(name="LIBELLE")
protected String libelle;
#Column(name="PIECE_JOINTE")
protected String piece_jointe;
#Embedded
protected Avis_Recette avis_recette;
public Encaissement(String encaiss_type, String libelle, String piece_jointe){
this.encaiss_type=encaiss_type;
this.libelle=libelle;
this.piece_jointe=piece_jointe;
}
public Encaissement(){
}
}
Encaissement_Espece, inherits from Encaissement
#Entity
#DiscriminatorValue("Espece")
#Table(name="ENCAISSEMENT_ESPECE")
public class Encaissement_Espece extends Encaissement{
public Caisse getCaisse() {
return caisse;
}
public void setCaisse(Caisse caisse) {
this.caisse = caisse;
}
public float getMontant() {
return montant;
}
public void setMontant(float montant) {
this.montant = montant;
}
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_CAISSE")
private Caisse caisse;
#Column(name = "MONTANT")
private float montant;
public Encaissement_Espece(float montant){
this.montant=montant;
}
public Encaissement_Espece(){
}
}
Encaissement_Technique, inherits from Encaissement
#Entity
#DiscriminatorValue("Technique")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="ENCAISS_TECHNIQUE_TYPE")
#Table(name="ENCAISSEMENT_TECHNIQUE")
public class Encaissement_Technique extends Encaissement implements Serializable{
public Banque getBanque() {
return banque;
}
public void setBanque(Banque banque) {
this.banque = banque;
}
public float getPrimeCoass() {
return primeCoass;
}
public void setPrimeCoass(float primeCoass) {
this.primeCoass = primeCoass;
}
public Set<Periode> getPeriode() {
return periode;
}
public void setPeriode(Set<Periode> periode) {
this.periode = periode;
}
public String getEncaiss_technique_type() {
return encaiss_technique_type;
}
public void setEncaiss_technique_type(String encaiss_technique_type) {
this.encaiss_technique_type = encaiss_technique_type;
}
#Column(name="PRIMECOASS")
protected float primeCoass;
#Column(name="ENCAISS_TECHNIQUE_TYPE")
protected String encaiss_technique_type;
public Encaissement_Technique(float primeCoass, String encaiss_technique_type){
this.primeCoass=primeCoass;
this.encaiss_technique_type=encaiss_technique_type;
}
public Encaissement_Technique(){
}
}
I hope i will find a pertinent answer as i searched for this in vain. It'll help me a lot.
Thank you.
"When i create an entity "Encaissement" with "Espece" as type, it is well created in the table Encaissement but it doesn't exist in the table Encaissement_espece." This statement suggests you have an instance of Encaissement and expect JPA to turn it into an instance of Encaissement_Espece just by changing the encaiss_type value. Java object inheritance doesn't work that way, which is what JPA inheritance tries to map to a relational database. An object in java cannot change what it is simply by setting a flag - you need to create a new instance if you want the data represented differently.
In this case, you need to create an instance of the Encaissement_Espece class. Because this class maps to the Encaissement and Encaissement_espece tables, JPA will automatically insert a row into both to represent this object. When you create Encaissement instance, a row goes into the Encaissement table, while when you create Encaissement_Technique instances, a row goes into both Encaissement_Technique and Encaissement tables. If you wish to change the object's type once it is persisted, you need to remove the old instance, flush, then persist the new one.
As mentioned in another answer, the encaiss_type is controlled through the class type itself and so does not need a mapping. Having one might be handy for queries or access (though you can just use instance of etc); it should be marked as insertable=false, updatable=false so that you do not attempt to modify the value directly.
remove the
#Column(name="ENCAISS_TYPE")
protected String encaiss_type;
from the Encaissment.
It will be handle automatically by JPA. It should solve the problem.

Categories