I got two entities FoaParamEmploye and FoaParamPosition (tables FOA_PARAM_EMPLOYE and FOA_PARAM_POSITION) with ManyToMany annotation (I didn't put here all attributes) :
#Entity
#Table(name = "FOA_PARAM_EMPLOYE")
#NamedQuery(name = "FoaParamEmploye.findAll", query = "SELECT f FROM FoaParamEmploye f")
public class FoaParamEmploye implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private FoaParamEmployePK id;
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_EMPLOYE"),
#JoinColumn(name = "COD_ENTREP") }
)
private List<FoaParamPosition> foaParamPositions;
}
And the second one :
#Entity
#Table(name="FOA_PARAM_POSITION")
#NamedQuery(name="FoaParamPosition.findAll", query="SELECT f FROM FoaParamPosition f")
public class FoaParamPosition implements Serializable {
#EmbeddedId
private FoaParamPositionPK id;
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_POSITION"),
#JoinColumn(name = "COD_ENTREP") }
)
private List<FoaParamEmploye> foaParamEmployes;
}
Association table is FOA_PARAM_EMPLOYE_POSITION with COD_ENTREP, ID_POSITION and ID_EMPLOYE fields.
PK are :
#Embeddable
public class FoaParamEmployePK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name="COD_ENTREP")
private String codEntrep;
#Column(name="ID_EMPLOYE")
private long idEmploye;
}
#Embeddable
public class FoaParamPositionPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name="COD_ENTREP")
private String codEntrep;
#Column(name="ID_POSITION")
private long idPosition;
}
I try to get all FoaParamPosition for a FoaParamEmploye. I wrote this JPQL query :
Query q = entityMgr.createQuery(
"SELECT position FROM FoaParamPosition position
INNER JOIN position.foaParamEmployes employes
where
employes.id.idEmploye = :idEmploye AND
employes.id.codEntrep =:codEntrep")
.setParameter("idEmploye", pIdEmploye)
.setParameter("codEntrep", "ENT");
I got an exception :
ORA-00904: "FOAPARAMEM1_"."FOAPARAMEMPLOYES_ID_EMPLOYE" : non valid identifier
As you can see, the generate SQL has this attribute but I can't understand why :
select
foaparampo0_.COD_ENTREP as COD1_2337_,
foaparampo0_.ID_POSITION as ID2_2337_,
foaparampo0_.ACTEUR_MAJ_OCCUR as ACTEUR3_2337_,
foaparampo0_.CD_PROFIL_AFFECTATION as CD4_2337_,
foaparampo0_.CD_TYPE_PROFIL_POSITION as CD5_2337_,
foaparampo0_.DATE_HEURE_MAJ_OCCUR as DATE6_2337_,
foaparampo0_.DT_FIN_ACTIVITE_POSITION as DT7_2337_,
foaparampo0_.HIERARCHIE_POSITION as HIERARCHIE8_2337_,
foaparampo0_.ID_DIVISION_AGENCE as ID9_2337_,
foaparampo0_.ID_EMPLOYE_PRINCIPAL as ID10_2337_,
foaparampo0_.ID_REF_EXT_POSITION_PARENTE as ID11_2337_,
foaparampo0_.ID_REF_EXTERNE_POSITION as ID12_2337_,
foaparampo0_.NIVEAU_AGENCE as NIVEAU13_2337_,
foaparampo0_.REF_EXT_POSITION as REF14_2337_,
foaparampo0_.xqcif as xqcif2337_
from
FOA_PARAM_POSITION foaparampo0_,
FOA_PARAM_EMPLOYE_POSITION foaparamem1_,
FOA_PARAM_EMPLOYE foaparamem2_
where
foaparampo0_.COD_ENTREP=foaparamem1_.ID_POSITION
and foaparampo0_.ID_POSITION=foaparamem1_.COD_ENTREP
and foaparamem1_.foaParamEmployes_COD_ENTREP=foaparamem2_.COD_ENTREP
and foaparamem1_.foaParamEmployes_ID_EMPLOYE=foaparamem2_.ID_EMPLOYE
and foaparamem2_.ID_EMPLOYE=?
and foaparamem2_.COD_ENTREP=?
I would recommend you to map your classes with some tool for example Netbeans -> generate JPA entities.
One of your side must be owning side (JoinColumn) and one must be inverse side (mappedBy).
If you don't wanna use any mapping tool, see this:
http://alextretyakov.blogspot.cz/2013/07/jpa-many-to-many-mappings.html
I'm not sure but I think if first you get an FoaParamEmploye object through below JPA query and after that use getFoaParamPosition method of this object to retrieve a list of positions your problem will get solved (when using this method in many-to-many association, the JPA will automatically retrieve the list):
Select distinct f from FoaParamEmployes f
where f.employes.idEmploye = :idEmploye
I don't see a problem in your mapping. The error you're getting complains explicitly about the invalid column name. In Oracle, the quoted names such as "FOAPARAMPO0_"."FOAPARAMPOSITIONS_ID_POSITION" are case sensitive, while the generated columns are foaParamPositions_COD_ENTREP and foaParamPositions_ID_POSITION and the case mismatch is causing your error.
A quick check would be to uppercase your foaParamEmployes and foaParamPositions and if I'm right the mentioned issue should be resolved. If so you can work-out a more elegant way to control the generated names with mappings.
I've noticed your other question in the comments referring to trouble with the inverseJoinColumns. That's why I'm mentioning upper-casing your properties. But in fact, you can handle it with mappings like
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_EMPLOYE"),
#JoinColumn(name = "COD_ENTREP") }
,
inverseJoinColumns = {#JoinColumn(name = "FOAPARAMPOSITION_COD_ENTREP"), #JoinColumn(name = "FOAPARAMPOSITION_ID_POSITION")}
)
private List<FoaParamPosition> foaParamPosition;
and
#ManyToMany
#JoinTable(
name = "FOA_PARAM_EMPLOYE_POSITION",
joinColumns = { #JoinColumn(name = "ID_POSITION"),
#JoinColumn(name = "COD_ENTREP") }
,
inverseJoinColumns = {#JoinColumn(name = "FOAPARAMEMPLOYE_COD_ENTREP"), #JoinColumn(name = "FOAPARAMEMPLOYE_ID_EMPLOYE")}
)
private List<FoaParamEmploye> foaParamEmploye;
which solves the issue in your other question I believe
Related
In my springboot application I have these 3 entities :
#Entity
public class Process {
#Id
private Long Id;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "input_id")
private Input input;
...
}
#Entity
public class Input{
#Id
private Long Id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "template_id")
private Template template;
...
}
#Entity
public class Template{
#Id
private Long Id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "template_id")
private Template template;
private String name;
...
}
In summary, Process has an FK to Input and Input has an FK to Template.
I would like to filter the processes whose template have a certain name. Here is the SQL I would to perform something like that:
select
*
from
process p
left outer join
input i
on p.input_id=i.id
left outer join
template t
on i.template_id=t.id
where
t.name='templateName'
Here is what I currently have in my Process entity to access the template :
#ManyToOne
#JoinTable(name = "Input",
joinColumns = {#JoinColumn(table = "Input", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "template_id", referencedColumnName = "id", table = "Template")})
private Template template;
Here is my ProcessRepository class, where I now have access to the desired find method :
#Repository
public interface ProcessRepository extends PagingAndSortingRepository<Process, Long> {
...
List<Process> findByTemplateNameEquals(String templateName);
...
}
When I execute the findByTemplateNameEquals method, I retrieve the process and one template. But the result I got was not the one expected.
I enabled the sql logging and here is the query really performed (I hide the columns, it is not important here):
select
...
from
process process0_
left outer join
input process0_1_
on process0_.id=process0_1_.id
left outer join
template template1_
on process0_1_.template_id=template1_.id
where
template1_.name=?
There is one problem with the join between Process and Input. It executes
from
process process0_
left outer join
input process0_1_
on process0_.id=process0_1_.id
instead of
from
process process0_
left outer join
input process0_1_
on process0_.input_id=process0_1_.id
I don't understand why it use the PK of Process instead of the FK to Input.
I tried several things to solve this :
Adding name="input_id" in the joinColumns = {#JoinColumn(... but instead of replacing the FK, it replaces the PK of input => failure during execution
replacing the referencedColumnName by "input_id" in the joinColumns = {#JoinColumn(... but it failed at launching.
Configuring a #ForeignKey(name = "input_id") at several places (directly in the #JoinTable, in the #JoinColumn and even in the #JoinColumn of the Input input attribute ) but there was no change.
I also remarked that the joinColumns = {#JoinColumn(table = "Input", referencedColumnName = "id")} was not necessary, because I have the same behaviour if I remove it.
Could someone help me on this ?
Many thanks in advance
I think the declaration of the template field at the Process level is probably unnecessary because you already have the relationship with Input, and certainly error prone.
In any case, if necessary, I would define the field with something like that instead:
#ManyToOne
#JoinTable(
name = "Input",
joinColumns = { #JoinColumn(name = "input_id")},
inverseJoinColumns = {#JoinColumn(name = "template_id")}
)
private Template template;
Pease, verify the code.
Having said that, if you only need to obtain the repositories associated with a certain template by name you can try to navigate through the object hierarchy in your find method. Please, try:
#Repository
public interface ProcessRepository extends PagingAndSortingRepository<Process, Long> {
...
List<Process> findByInput_Template_Name(final String templateName);
...
}
I have two entities mapped to one another using the oneToMany annotation. One entity is bookedBus and the second is drivers The drivers entity would already have a row inserted into that would later become a foreign reference (FK) to bookedBus entity(PK). Below are the two entities, setters and getter have been skipped for brevity.
First entity
#Entity
#Table(name = "bookedBuses")
public class BookedBuses implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "driver_id")
private Drivers driver;
}
Second entity
#Entity
public class Drivers implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "driver")
private List<BookedBuses> bookedBus;
}
Now When I try to save to the booked bus entity it throws the following exception
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.bus.api.entity.Drivers; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.bus.api.entity.Drivers
Below is how I tried saving to the bookedBus entity
BookedBuses bookedRecord = new BookedBuses();
bookedRecord.setBookedSeats(1);
bookedRecord.setBookedBusState(BookedBusState.LOADING);
bookedRecord.setBus(busService.getBusByPlateNumber(booking.getPlateNumber()));
bookedRecord.setRoute(booking.getRoute());
infoLogger.info("GETTING DRIVER ID ======= " + booking.getDriver().getId());
Drivers drivers = new Drivers(booking.getDriver().getId());
List<BookedBuses> d_bu = new ArrayList<>();
drivers.setBooked(d_bu);
drivers.addBooked(bookedRecord);
bookedRecord.setDriver(drivers);
bookedBusService.save(bookedRecord);
My BookBusService Save Method as requested
#Autowired
private BookedBusRepository bookedBusRepo;
public boolean save(BookedBuses bookedRecord) {
try {
bookedBusRepo.save(bookedRecord);
return true;
} catch (DataIntegrityViolationException ex) {
System.out.println(ex);
AppConfig.LOGGER.error(ex);
return false;
// Log error message
}
}
1st you have some mix up in naming: you have Driver & Drivers. Like this:
private Drivers driver;
Also selecting variable names like this:
BookedBuses bookedRecord = new BookedBuses();
will cause a lot of confusion. Do not mix plural & singular between types and preferably also do not introduce names that might not be easily associated like record. Also this:
private List<BookedBuses> bookedBus;
which should rather be like:
private List<BookedBus> bookedBuses;
(and would alsoi require change to your class name BookedBuses -> BookedBus)
Anyway the actual problem seems to lie here:
Drivers drivers = new Drivers(booking.getDriver().getId());
You need to fetch existing entity by id with a help of repository instead of creating a new one with id of existing. So something like:
Drivers drivers = driverRepo.findOne(booking.getDriver().getId()); // or findById(..)
It seems that you have a constructor (that you did not show) that enables to create a driver with id. That is not managed it is considered as detached. (You also have drivers.addBooked(bookedRecord); which you did not share but maybe it is trivial)
Note also some posts suggest to changeCascadeType.ALL to CascadeType.MERGE whether that works depends on your needs. Spring data is able to do some merging on save(..) based on entity id but not necessarily in this case.
This line
Drivers drivers = new Drivers(booking.getDriver().getId());
If you already have the driver ID available with you then there's no need to pull the driver ID again from the DB.
After removing the Cascade attribute from #OneToMany & #ManyToOne your code should work.
#Entity
#Table(name = "bookedBuses")
public class BookedBuses implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
`
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "driver_id")
private Drivers driver;
}
#Entity
public class Drivers implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "driver_id")
private List<BookedBuses> bookedBus;
}
I have two java-classes / db-tables: 'message' and 'thirdparty'
#Entity
public class Message {
#OneToOne(mappedBy = "message")
private ThirdParty source = null;
#OneToOne(mappedBy = "message")
private ThirdParty target = null;
....
}
#Entity
public class ThirdParty {
#OneToOne(targetEntity = Message.class)
#JoinColumn(name = "Message", referencedColumnName = "mess_id", nullable = false)
private Message message = null;
#Column(name = "isSource", nullable = false)
private Boolean isSource = null;
}
Message has two references to ThirdParty, which could be differenced by isSource (if they are source or target).
This cannot be resolved by jpa they way it is designed / annotiated. But is there a way to to this by adding some annotiation or some kind of special sql-statement?
This is conceptually wrong. You cannot do this. OneToOne mapping occurs when there are two entities mapped in the following way :
Entity1 : Has a primary key(PK1) and others along with a foreign key(FK)
Entity2 : Has a primary key (PK2).
Now the FK is mapped to PK2 in such a way that for each occurence of PK there must be a one and only one matching occurence of FK.
I have three tables with entities in hibernate. DB - MySQL. I need to get fields from entity "Item" where ModelsMm.id has some value. At first I tried to do separate queries, it was huge amount of requests in sum. So, i tried to do complex query, but it became a very long run.
I think there is a simpler way, but I do not know what.
My query and entities.
List<Item> itemIds = session.createQuery("select it from Item it where :id in elements(it.mmPrice.modelsMm)");
#Entity (name = "MODELS_MM")
public class ModelsMm {
#Id
private int Id;
#ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name="parth_mm", joinColumns={#JoinColumn(name="MODEL_ID", referencedColumnName="ID")}, inverseJoinColumns={#JoinColumn(name="PART_ID", referencedColumnName="ID")})
private List<MmPrice> mmPrices;
#Entity (name = "MM_PRICE")
public class MmPrice {
#Id
private int id;
private String article;
#OneToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "article", referencedColumnName = "article",insertable = false, updatable = false)
private Item item;
#ManyToMany
#JoinTable(name="parth_mm", joinColumns={#JoinColumn(name="PART_ID", referencedColumnName="ID")}, inverseJoinColumns={#JoinColumn(name="MODEL_ID", referencedColumnName="ID")})
private List<ModelsMm> modelsMm;
#Entity
#Table(name="SHOP_ITEMS")
public class Item implements Serializable {
#Id
private int id;
private String article;
#OneToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "article", referencedColumnName = "article",insertable = false, updatable = false)
private MmPrice mmPrice;
In console i have that query
Hibernate: select item0_.ID as ID0_, item0_.ARTICLE as ARTICLE0_, item0_.article as article0_ from SHOP_ITEMS item0_ cross join MM_PRICE mmprice1_ where item0_.article=mmprice1_.article and (? in (select modelsmm2_.MODEL_ID from parth_mm modelsmm2_ where mmprice1_.ID=modelsmm2_.PART_ID))
Thanks.
First, you'll have to fix your mapping. In a bidirectional association, one side MUST be the inverse side, and thus use the mappedBy attribute. For example, if you choose ModelsMm to be the inverse side, then its mmPrices attribute should be declared as
#ManyToMany(mappedBy = "modelsMm")
private List<MmPrice> mmPrices;
You should also forget about CascadeType.ALL on ManyToMany associations: it makes no sense. You don't want to delete all the courses of a student when you delete a student, since the course is also followed by several other students.
Now, regarding your query, it's not very clear what you want to do. If you want to select all the items which have a price which have at least one model whose ID is in a collection of IDs, then you simply need the following query:
select distinct i from Item i
join i.mmPrice p
join p.modelsMm m
where m.id in :modelIds
Side note: please fix your naming. This inconsistent and unnecessary usage of mm as a prefix or suffix makes the code unreadable. Name your class Price, the fields of type Price price, and the collections of prices prices. Just as you would do in English: an Item has a price, and a price has models.
Simplifying, in my database I have tables:
Car (pk="id_car")
CarAddon (pk="id_car_fk,id_addon_fk",
`FK_car_addon_addon` FOREIGN KEY (`id_addon_fk`) REFERENCES `addon` (`id_addon`)
`FK_car_addon_car` FOREIGN KEY (`id_car_fk`) REFERENCES `car` (`id_car`)
Addon (pk="id_addon")
Shortly: I have cars, many cars can has many addons (like ABS etc).
There are tables with cars, addons, and one table which is logical connection.
Overall, entities work fine. I have no problems with persist data, when I want persist single object. I don't have problems, when I want FETCH data, ie. Car->getAddon();
But, when I'm going to persisting a collection, nothing happens. No exceptions were thrown, there were no new data in database.
//DBManager is a singleton to create an EntityManager
EntityManager em = DBManager.getManager().createEntityManager();
em.getTransaction().begin();
Addon addon1 = new Addon();
addon1.setName("czesc1");
em.persist(addon1);
Addon addon2 = new Addon();
addon2.setName("czesc2");
em.persist(addon2);
car.setAddonCollection(new ArrayList<Addon>());
car.getAddonCollection().add(addon1);
car.getAddonCollection().add(addon2);
em.persist(car);
em.getTransaction().commit();
In this case, addons were stored in Addon table, car in Car table. There are no new data in CarAddon table though object car has good data (there is addon collection in debbuger).
When I changed em.persist(car) to em.merge(car) I got an exception:
"SEVERE: Persistence error in /admin/AddAuction : java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: model.entity.Car[ idCar=0 ]."
Simple version of my classess:
#Entity
#Table(name = "addon")
#XmlRootElement
#NamedQueries({...})
public class Addon implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#NotNull
#Column(name = "id_addon")
private Integer idAddon;
#Size(max = 100)
#Column(name = "name")
private String name;
#JoinTable(name = "car_addon",
joinColumns = {
#JoinColumn(name = "id_addon_fk", referencedColumnName = "id_addon")},
inverseJoinColumns = {
#JoinColumn(name = "id_car_fk", referencedColumnName = "id_car")})
#ManyToMany
private List<Car> carCollection;
#XmlTransient
public List<Car> getCarCollection() {
return carCollection;
}
public void setCarCollection(List<Car> carCollection) {
this.carCollection = carCollection;
}
}
#Entity
#Table(name = "car")
#XmlRootElement
#NamedQueries({...)
public class Car implements Serializable {
#ManyToMany(mappedBy = "carCollection", fetch= FetchType.EAGER, cascade=CascadeType.ALL)
private List<Addon> addonCollection;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#NotNull
#Column(name = "id_car")
private Integer idCar;
#XmlTransient
public List<Addon> getAddonCollection() {
return addonCollection;
}
public void setAddonCollection(List<Addon> addonCollection) {
this.addonCollection = addonCollection;
}
}
How can I fix it?
ps1. I have:
cascade=CascadeType.ALL przy #ManyToMany private List<Car> carCollection
but this dos not solve my problem.
ps2. I am using Netbeans 7, EclipseLink and MySQL (not Hibernate - I have problem with it)
I have one theory that always seems to trip people up with many-to-many collections. The problem is that in memory, the associations are made in two places. Both in the car's addons list and in the addon's cars list. In the database, there isn't such a duplication.
The way JPA providers get around this is through the mappedBy attribute. Since you have mappedBy on the car's addons list this means that the relationship is actually controlled by the addon's cars list (confusing I know).
Try adding the following:
addon1.setCarCollection(new ArrayList<Car>());
addon1.getCarCollection().add(car);
addon2.setCarCollection(new ArrayList<Car>());
addon2.getCarCollection().add(car);
before you persist the car.
Generally speaking, I would avoid many-to-many associations. What you really have is an intermediate link table, with a one-to-many and a many-to-one. As soon as you add anything of interest to that link table (e.g. datestamp for when the association was made), poof, you are no longer working with a pure many-to-many. Add in the confusion around the "owner" of the association, and you're just making things a lot harder than they should be.
could you try add
#JoinTable(name = "car_addon",
joinColumns = {
#JoinColumn(name = "id_addon_fk", referencedColumnName = "id_addon")},
inverseJoinColumns = {
#JoinColumn(name = "id_car_fk", referencedColumnName = "id_car")})
to both side
just reverse the joinColumns and inverseJoinColumns
Try adding (fetch = FetchType.EAGER) to your ManyToMany annotation