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.
Related
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();
I am currently defining JPA entities for a legacy database (lots of composite keys, but also single-column keys). I have created the following entity superclass:
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> {
public abstract ID getId();
public abstract void setId(ID id);
}
And then a superclass for composite keys (as well as a superclass for long primary key, not listed here):
#MappedSuperclass
public abstract class AbstractEmbeddedIdEntity<ID extends Serializable> extends AbstractEntity<ID> {
#EmbeddedId
private ID id;
public AbstractEmbeddedIdEntity() {
id = newId();
}
#Override
public ID getId() {
return id;
}
#Override
public void setId(ID id) {
this.id = id;
}
protected abstract ID newId();
}
And finally concrete entities like this:
#Entity
#Table(name = "firstEntity")
public class FirstEntity extends AbstractEmbeddedIdEntity<FirstEntityId> {
public FirstEntity() {
}
#Embeddable
public static class FirstEntityId implements Serializable {
#Column(name = "firstId")
private String firstId;
public FirstEntityId() {
}
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof FirstEntityId)) {
return false;
}
FirstEntityId other = (FirstEntityId) obj;
return
Objects.equals(firstId, other.firstId);
}
#Override
public int hashCode() {
return Objects.hash(firstId);
}
}
#Override
protected FirstEntityId newId() {
return new FirstEntityId();
}
}
Now the issue is that if I have multiple entities like this and try to access an ID property of an entity (currently with Spring Boot, e.g. findByIdFirstId(String firstId)), an exception is thrown:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [firstId] on this ManagedType [unknown]
I have debugged this and found out that in hibernate, the metamodel maps all of my entities to the same MappedSupperclass instance. During application startup, the #EmbeddedId returned by newId() is set to the MappedSupperclass, overwriting the ID of the previous entity. So in the end, all entities are mapped to the same MappedSupperclass, but the MappedSupperclass only has the #EmbeddedId of the last entity.
In the above example, accessing the ID property fails because the #EmbeddedId of the last entity doesn't have a property called "firstId" (it has been overwritten with the ID properties of the last entity).
Now I am wondering if my approach is wrong, if I am missing something or if this could be an issue with hibernate?
Complete example using spring boot available on github. Run with mvn spring-boot:run.
This looks to me like a bug in hibernate, therefore I have created a ticket in the hibernate bug tracker.
As a workaround I am now defining the ID attribute (#EmbeddedId) in the concreate entity classes instead of the abstract superclass.
I would like to create a database with JPA. My problem is I can't figure it out how to configure the entities so :
if a project is deleted the employees stays in the database
if an employee is deleted the project stays in the database if there is at least one employee still working on the project
I have two entities like:
#Entity(name = "PROJECTS")
public class Project implements Serializable {
...
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name="PROJ_EMPL",
joinColumns=
#JoinColumn(name="PROJECT_ID", referencedColumnName="PROJECT_ID"),
inverseJoinColumns=
#JoinColumn(name="EMPLOYEE_ID", referencedColumnName="EMPLOYEE_ID")
)
private List<Employee> employeeList;
...
#Entity(name = "EMPLOYEES")
public class Employee implements Serializable {
...
#ManyToMany(mappedBy="employeeList",fetch=FetchType.EAGER)
private List<Project> projectList;
...
As said in the comment, I don't think you can do this utilizing JPA alone and I argue it shouldn't be possible.
Conceptually, what you specified is a invariant of your Project: "Have at least one member in employeeList."
I like entities that protect their own invariants, maybe like so:
public class Project {
#ManyToMany
private List<Employee> employeeList = new ArrayList<>();
private Project(){ } /* we need this for Hibernate */
public Project(Collection<Employee> initialMembers) {
employeeList.addAll(initialMembers);
}
public boolean removeEmployee(Employee e) {
if (employeeList.size() <= 1) return false;
employeeList.remove(e);
return true;
}
public int employeesCnt() {
return employeeList.size();
}
}
Application logic needs to implement your requirement:
public ProjectController {
private Project instance = /* get your project somehow */;
#Transactional
public void reduceEmployeeCnt() {
if (!instance.removeEmployee()) {
jpaSession.remove(instance);
}
}
}
Another way would be a EntityListener #PreRemove like described here.
I am exploring JPA and Ebean within Play Framework 2.0.1 to research what is generated in a local relational database. In my tests, I'm using MariaDB, a fork of MySQL.
So far, I've been able to successfully generate tables in the database using the JPA annotations within my models. I wanted to make sure that I learned how to set up an entity with a OneToOne relationship to another entitiy, as well as a OneToMany to another.
OneToOne: Employee has Address
OneToMany: Employee has Pet(s)
I set up a small application that allows me to quickly populate the tables, display some of the info, and delete rows from the tables. Here is the code below for those who want to see my setup:
Routes
GET / controllers.Application.index()
GET /add controllers.Application.addEmployee()
GET /delete/:id controllers.Application.deleteEmployee(id: Long)
GET /employee/:id controllers.Application.showEmployee(id: Long)
Controller
Application.java
public class Application extends Controller {
public static Result index() {
return ok(index.render("entry point"));
}
public static Result addEmployee() {
// Create an employee.
Employee employee = new Employee();
employee.firstName = "John";
employee.lastName = "Doe";
employee.salary = new BigDecimal(123456);
// Create an address for the employee.
Address address = new Address();
address.city = "West Chester";
address.country = "United States of America";
address.postalCode = "19380";
address.province = null;
address.street = "45 Jingleheimer Drive";
// Create pets for the employee.
Pet pet = new Pet();
pet.petString = "dog";
Pet pet2 = new Pet();
pet2.petString = "cat";
employee.address = address;
employee.pets.add(pet);
employee.pets.add(pet2);
employee.save();
return ok(index.render("added an employee"));
}
public static Result showEmployee(Long id) {
Employee e = Employee.get(id);
//String s = e.address.street;
return ok(employee.render(e));
}
public static Result deleteEmployee(Long id) {
Employee.delete(id);
return ok(index.render("deleted employee " + id));
}
}
Models
Employee.java
#Entity
public class Employee extends Model {
#Id
public Long employee_id;
public String firstName;
public String lastName;
public BigDecimal salary;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="owner_id")
public Address address;
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="owner_id", referencedColumnName="employee_id")
public List<Pet> pets;
public static Employee get(Long id) {
return find.byId(id);
}
public static void delete(Long id) {
find.byId(id).delete();
}
public static Finder<Long, Employee> find = new Finder<Long,Employee>(Long.class, Employee.class);
}
Address.java
#Entity
public class Address extends Model {
#Id
public Long address_id;
public String street;
public String city;
public String province;
public String country;
public String postalCode;
}
Pet.java
#Entity
public class Pet extends Model {
#Id
public Long pet_id;
public String petString;
}
Views
employee.scala.html
#(employee: models.Employee)
#import helper._
#main("employee view") {
<p>#employee.firstName</p>
<p>#employee.lastName</p>
<p>#employee.salary</p>
<p>#employee.address.country</p>
<p>#employee.pets(1).petString</p>
#for(pet <- employee.pets) {
<p>#pet.petString</p>
}
}
If I render the view above using a URL like localhost:9000/employee/1 I get the following displayed in the browser:
John
Doe
123456
cat
dog
cat
Notice that the address's country is not displayed. I've also tried accessing it in the controller and printing it to the command window, which prints NULL.
However, I found that if I added getters to the Address class, I can retrieve it in the scala template:
Adding
public String getCity() {
return city;
}
...
to Address.java results in
John
Doe
123456
United States of America
cat
dog
cat
So I am able to set an address and assign it to an employee (it shows up in my DB). Also, deleting an employee correctly cascades and deletes the Address in the database. Likewise, this works for the Pet class which is OneToMany. So why do I need to add getters to access my Address properties, but not for Pet? Is my one-to-one relationship not set up correctly in the Java annotations?
EBean does not support lazy loading with direct property access.
Within Java-Controller-Code, the statement employee.address.country is enhanced to something like employee.getAddress().getCountry(). This, alas, is not done in Scala code, specifically not in the templates.
employee.pets(1).petString, on the other hand, is translated to employee.pets.get(1).petString, which is enough for EBean to lazy load, because an EBean LazyList is involved.
Don't stick back to getters/setters please! The Play! people argue you should know in the controller what data you need and fetch it at query time:
public static Employee get(Long id) {
return find.fetch("address").where().idEq(id).findUnique();
}
I have been looking into JPA/Hibernate #Entity inheritance for a while now and can't seem to find anything that addresses what I am trying to achieve.
Basically I want to be able to define an #Entity with all of the column and table mappings as required. Then I want to be able to extend the #Entity in a number of different locations with different sets of #Transient methods defined in the body of each "sub-Entity". This is a basic example of what I am trying to achieve but with no success thus far:
#Entity
#Table(name = "mountain")
public class MountainEntityBase implements Serializable {
public Integer mountainId = 0;
public Integer height = 0;
public List<ExplorerEntityBase> explorers = new ArrayList<ExplorerEntityBase>();
#Id
#GeneratedValue
#Column(name = "mountain_id")
public Integer getMountainId() { return mountainId; }
public void setMountainId(Integer mountainId) { this.mountainId = mountainId; }
#Column(name="height")
public String getHeight() { return height; }
public void setHeight(String height) { this.height = height; }
#OneToMany(mappedBy="mountainId")
public List<ExplorerEntityBase> getExplorers() { return this.explorers; }
public void setExplorers(List<ExplorerEntityBase> explorers) { this.explorers = explorers; }
}
.
#Entity
public class MountainEntity extends MountainEntityBase implements Serializable {
public List<MountainEntity> allMountainsExploredBy = new ArrayList<MountainEntity>();
#Transient
public List<MountianEntity> getAllMountainsExploredBy(String explorerName){
// Implementation
}
}
So any extended class will define only #Transients in its body. But also I want to allow for situations where the child class is empty:
#Entity
public class MountainEntity extends MountainEntityBase implements Serializable {
}
Thanks in advance for any help with this.
Inheritance in JPA is specified on the root entity using the #Inheritance annotation. There you can specify the database representation of the hierarchy. Check the documentation for more details.
If your child classes define only transient fields (not methods) (i.e. not saved in the db), then perhaps a discriminator column is the best option. But it may be the case that you don't actually need inheritance - the main entity can have all the methods (because it has all the fields the methods operate on)