I'm trying to create a RESTful service. For multi tenancy I'm applying the #AdditionalCriteria annotation. However, when I join an entity using the #OneToOne annotation the following Exception is raised:
Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.3.0.v20110604-
r9504): org.eclipse.persistence.exceptions.QueryException
Exception Description: No value was provided for the session property
[SECURITYID]. This exception is possible when using additional criteria or
tenant discriminator columns without specifying the associated contextual
property. These properties must be set through Entity Manager, Entity Manager
Factory or persistence unit properties. If using native EclipseLink, these
properties should be set directly on the session.
Query: ReadObjectQuery(name="readObject" referenceClass=Addresses
sql="SELECT id, city, country, postalcode, province, security_id, street
FROM addresses WHERE ((id = ?) AND (security_id = ?))")
I set the property [SECURITYID] at EntityManager level and without the join everything works just fine. But when I join an entity using the #OneToOne I see that property is not there. I don't have enough experience to determine if this is caused by me doing something wrong or if this is a bug. To me it looks like a different EntityManager is used to fetch the joined entity. But I'm guessing because of me lacking in knowledge. I also tried to set the properties on EntityManagerFactory level but to no avail.
Here is my setup.
Entity:
#Entity
#AdditionalCriteria("this.securityId=:SECURITYID")
#Table(name = "tasks", catalog = "catalog", schema = "schema")
#XmlRootElement
#NamedQueries(
{
#NamedQuery(name = "Tasks.findAll", query = "SELECT t FROM Tasks t")
})
public class Tasks implements Serializable
{
...
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="service_address_id")
private Addresses serviceAddress;
...
}
RESTFacade class
#Stateless
#Path("tasks")
public class TasksFacadeREST extends AbstractFacade<Tasks>
{
#PersistenceContext(unitName = "UnitName")
private EntityManager em;
...
#java.lang.Override
protected EntityManager getEntityManager()
{
// Temp! REMOVE WHEN DONE
sessionId = "123456789";
Identifier.setIdentity(em,sessionId);
em.setProperty("SECURITYID",Identifier.securityId);
em.setProperty("USERID",Identifier.userId);
return em;
}
Thanks & Rgds,
M
My guess is this is related to caching.
Normally the tenant property would be set on the EntityManagerFactory, not the EntityManager. This would mean you have a different EntityManagerFactory for each tenant. You could also define the tenant property in your persistence.xml and have a different persistence unit per tenant.
If you need to set the tenant per EntityManager, then you need to disable the shared cache, or setting the cache mode to protected.
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Single-Table_Multi-Tenancy#Setting_Properties_and_Caching_Scope
For reference the property to be disabled is eclipselink.cache.shared.default
<property name="eclipselink.cache.shared.default" value="false"/>
Related
How can I avoid unnecessary queries to the DB?
I have LoadEntity with two nested entity - CarrierEntity and DriverEntity. Java class:
#Entity
public class LoadEntity {
...
#ManyToOne
#JoinColumn(name="carrier_id", nullable=false)
private CarrierEntity carrierEntity;
#ManyToOne
#JoinColumn(name="driver_id", nullable=false)
private DriverEntity driverEntity;
}
But API send me carrierId and driverId. I make it:
DriverEntity driverEntity = driverService.getDriverEntityById(request.getDriverId());
loadEntity.setDriverEntity(driverEntity);
loadRepository.save(loadEntity);
How can I write only driverId with JPA?
With Spring Data JPA you can always fall back on plain SQL.
Of course, this will side step all the great/annoying logic JPA gives you.
This means you won't get any events and the entities in memory might be out of sync with the database.
For this reason you might also increase the version column, if you are using optimistic locking.
That said you could update a sing field like this:
interface LoadRepository extends CrudRepository<LoadEntity, Long> {
#Query(query="update load_entity set driver_id = :driverId where carrier_id=:carrier_id", nativeQuery=true)
#Modifying
void updateDriverId(Long carrierId, Long driverId);
}
If you just want to avoid the loading of the DriverEntity you may also use JpaRepository.getById
Before updating an entity in my Jakarta EE application running on GlassFish 5.1.0 with EclipseLink 2.7.4 and Derby 10.14.2.0, I compare the updated entity to the saved entity and document the changes. I noticed recently that my compare code was not working with #OneToMany relationship properties and #ElementCollection properties, and I tracked the problem to Lazy loading of the #OneToMany and #ElementCollection properties. I was able to resolve the using the fetch attribute as follows:
Fetch Eager Entity
#Entity
public class Container implements Serializable {
#OneToMany(mappedBy = "container", fetch = FetchType.EAGER)
private List<AssetSerial> assets;
#ElementCollection (fetch = FetchType.EAGER)
private List<Reference> references;
I wasn't entirely happy with this solution, because I assumed that the developers defaulted these relationship types to lazy loading for a purpose, so I continued researching and was excited to find many references to JPA Entity Graphs. I immediately create the following code to force EclipseLink to initialize my lazy loading properties before documenting the entity changes.
Entity Graph Entity
#Entity
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#NamedEntityGraph(
name = "Container.eager",
attributeNodes = {
#NamedAttributeNode("assets"),
#NamedAttributeNode("references") })
public class Container implements Serializable {
Entity Manager Initialization
#PersistenceContext(unitName = "MYPU")
private EntityManager em;
Find Method that only Works once per Entity
Map<String, Object> props = new HashMap<String, Object>();
props.put("javax.persistence.loadgraph", em.getEntityGraph("Container.eager"));
Container managedContainer = em.find(Container.class, updatedContainer.getId(), props);
PersistenceUnitUtil tester = em.getEntityManagerFactory().getPersistenceUnitUtil();
logger.debug("Assets: {}", tester.isLoaded(managedContainer, "assets"));
logger.debug("References: {}", tester.isLoaded(managedContainer, "references"));
Unfortunately, the isLoaded test methods only return true the first time I call the find method on a specific entity. The second and subsequent times isLoaded returns false. I struggled with this issue for many hours and determined that this issue was that the EclipseLink shared cache was not honoring the entity graph hint I was passing to the find method. I solved the problem by evicting the entity from the cache immediately before calling the find as shown below.
Find Method that Works
em.getEntityManagerFactory().getCache().evict(Container.class, updatedContainer.getId());
Map<String, Object> props = new HashMap<String, Object>();
props.put("javax.persistence.loadgraph", em.getEntityGraph("Container.eager"));
Container managedContainer = em.find(Container.class, updatedContainer.getId(), props);
PersistenceUnitUtil tester = em.getEntityManagerFactory().getPersistenceUnitUtil();
logger.debug("Assets: {}", tester.isLoaded(managedContainer, "assets"));
logger.debug("References: {}", tester.isLoaded(managedContainer, "references"));
Now the isLoaded test always returns true, and I'm able to document all the changed in the updated entity.
In summary, I have the following questions:
Why is EclipseLink not honoring my entity graph?
Am I going to encountering problems manually evicting my entity from the cache?
Is there a better way to force EclipseLink to initialize my lazy loading properties?
I have a web application built with Spring Boot connected with a db PostgreSql, the project is about a education institute and manage Students and invoicing...
I generate manually all the invoices. I add the first cuota(invoice) normally but when i want to generate the second, i've this issue:
java.lang.IllegalStateException: Multiple representations of the same entity [com.codeboros.app.entity.Cuota#1] are being merged. Detached: [com.codeboros.app.entity.Cuota#1788e1df]; Managed: [com.codeboros.app.entity.Cuota#2697e3fc]
I've this entities:
#Entity
#Table(name="ALUMNOS")
public class Alumno implements Serializable {
#OneToMany(mappedBy="alumno", cascade= {CascadeType.DETACH,CascadeType.PERSIST,CascadeType.DETACH,CascadeType.REMOVE,CascadeType.REFRESH,CascadeType.MERGE}, fetch=FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JsonIgnore
private List<Cuota> cuotas;
}
#Entity
#DiscriminatorValue(value="Cuota")
public class Cuota extends Factura implements Serializable {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="alumno_cod")
#NotFound(action = NotFoundAction.IGNORE)
Alumno alumno;
}
And the AlumnoController
#PostMapping("/alumnoGenCuota/{id}")
public String GenCuota(#PathVariable Long id, Cuota cuota) {
Alumno alumno = alumnoService.get(id);
cuota.setAlumno(alumno);
cuota.setMonto(alumno.getCurso().getCuota());
cuota.setDetalle(alumno.getCurso().getNombre()+": $"+alumno.getCurso().getCuota()); //detalle
alumno.AgregarCuota(cuota);
alumnoService.save(alumno);
return "redirect:/alumnocuotas/"+id;
}
I tried to remove CascadeType.MERGE but do not save the news Cuotas
If you are not using Hibernate, remove CascadeType.MERGE from the entity that is not allowing you to persist the detached entity or put all cascade type other than CascadeType.MERGE
If you are using Hibernate, add the following lines to your persistence.xml -
<property name="hibernate.event.merge.entity_copy_observer" value="allow"/>
When you set hibernate.event.merge.entity_copy_observer=allow, Hibernate will merge each entity copy detected while cascading the merge operation. In the process of merging each entity copy, Hibernate will cascade the merge operation from each entity copy to its assocations with cascade=CascadeType.MERGE or CascadeType.ALL. The entity state resulting from merging an entity copy will be overwritten when another entity copy is merged.
Here are my entities:
#Entity
public class Actor {
private List<Film> films;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="actor_id"),
inverseJoinColumns = #JoinColumn(name="film_id"))
public List<Film> getFilms(){
return films;
}
//... more in here
Moving on:
#Entity
public class Film {
private List actors;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="film_id"),
inverseJoinColumns = #JoinColumn(name="actor_id"))
public List<Actor> getActors(){
return actors;
}
//... more in here
And the join table:
#javax.persistence.IdClass(com.tugay.sakkillaa.model.FilmActorPK.class)
#javax.persistence.Table(name = "film_actor", schema = "", catalog = "sakila")
#Entity
public class FilmActor {
private short actorId;
private short filmId;
private Timestamp lastUpdate;
So my problem is:
When I remove a Film from an Actor and merge that Actor, and check the database, I see that everything is fine. Say the actor id is 5 and the film id is 3, I see that these id 's are removed from film_actor table..
The problem is, in my JSF project, altough my beans are request scoped and they are supposed to be fetching the new information, for the Film part, they do not. They still bring me Actor with id = 3 for Film with id = 5. Here is a sample code:
#RequestScoped
#Named
public class FilmTableBackingBean {
#Inject
FilmDao filmDao;
List<Film> allFilms;
public List<Film> getAllFilms(){
if(allFilms == null || allFilms.isEmpty()){
allFilms = filmDao.getAll();
}
return allFilms;
}
}
So as you can see this is a request scoped bean. And everytime I access this bean, allFilms is initially is null. So new data is fetched from the database. However, this fetched data does not match with the data in the database. It still brings the Actor.
So I am guessing this is something like a cache issue.
Any help?
Edit: Only after I restart the Server, the fetched information by JPA is correct.
Edit: This does not help either:
#Entity
public class Film {
private short filmId;
#ManyToMany(mappedBy = "films", fetch = FetchType.EAGER)
public List<Actor> getActors(){
return actors;
}
The mapping is wrong.
The join table is mapped twice: once as the join table of the many-to-many association, and once as an entity. It's one or the other, but not both.
And the many-to-many is wrong as well. One side MUST be the inverse side and use the mappedBy attribute (and thus not define a join table, which is already defined at the other, owning side of the association). See example 7.24, and its preceeding text, in the Hibernate documentation (which also applies to other JPA implementations)
Side note: why use a short for an ID? A Long would be a wiser choice.
JB Nizet is correct, but you also need to maintain both sides of relationships as there is caching in JPA. The EntityManager itself caches managed entities, so make sure your JSF project is closing and re obtaining EntityManagers, clearing them if they are long lived or refreshing entities that might be stale. Providers like EclipseLink also have a second level cache http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching
I have a very basic relationship between two objects:
#Entity
public class A {
#ManyToOne(optional = false)
#JoinColumn(name="B_ID", insertable=false, updatable=true)
private StatusOfA sa;
getter+setter
}
#Entity
public class StatusOfA {
#Id
private long id;
#Column
private String status;
getter+setter
}
There's only a limited set of StatusOfA in DB.
I perform an update on A in a transaction:
#TransactionalAttribute
public void updateStatusOfA(long id) {
A a = aDao.getAById(123);
if(a != null) {
a.getStatusOfA().getId(); //just to ensure that the object is loaded from DB
StatusOfA anotherStatusOfA = statusOfADao.getStatusOfAById(456);
a.setStatusOfA(aontherStatusOfA);
aDao.saveOrPersistA(a);
}
}
The saveOrPersistA method is here merging 'a'.
I expect Eclipselink to perform only an update on 'a' to update the StatusOfA but it's executing a new insert on StatusOfA table. Oracle is then complaining due to a unique contraint violation (the StatusOfA that Eclipselink tries to persist already exists...).
There is no Cascading here so the problem is not there and Hibernate (in JPA2) is behaving as excepted.
In the same project, I already made some more complex relationships and I'm really surprised to see that the relation here in not working.
Thanks in advance for your help.
What does, statusOfADao.getStatusOfAById() do?
Does it use the same persistence context (same transaction and EntityManager)?
You need to use the same EntityManager, as you should not mix objects from different persistence contexts.
What does saveOrPersistA do exactly? The merge() call should resolve everything correctly, but if you have really messed up objects, it may be difficult to merge everything as you expect.
Are you merging just A, or its status as well? Try also setting the status to the merged result of the status.
Assumptions: #Id#GeneratedValue(strategy = GenerationType.IDENTITY)
Let's consider the following implementations of statusOfADao.getStatusOfAById(456) :
1. returns "proxy" object with just id set:
return new StatusOfA(456);
2. returns entity in new transaction:
EntityManager em = emf.createEntityManager();em.getTransaction().begin();
StatusOfA o = em.find(StatusOfA.class,456);//em.getReference(StatusOfA.class,456);
em.getTransaction().commit();
return o;
3. returns detached entity:
StatusOfA o = em.find(StatusOfA.class,456);//em.getReference(StatusOfA.class,456);
em.detached(o);
return o;
4. returns deserialized-serialized entity:
return ObjectCloner.deepCopy(em.find(StatusOfA.class,456));
5. returns attached entity:
return em.find(StatusOfA.class,456);
Conclusions:
Eclipselink handles only implementation N5 as "expected".
Hibernate handles all five implementations as "expected".
No analisys of what behaviour is jpa spec compliant