CreateQuery joining 2 tables in JPA - java

I have this error
The collection-valued path 'c.medecin' cannot be resolved to a valid association field
The state field path 'm.id' cannot be resolved to a valid type.
when I execute this request
createQuery("select c from Creneaux c join c.medecin m where m.id=:idMedecin").setParameter("idMedecin", medecin.getId());
I use these 2 tables : MEDECINS(ID) and CRENEAUX(ID, ID_MEDECIN)
#Entity
#Table(name = "medecins")
#XmlRootElement
public class Medecins implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "medecin"/*"idMedecin"*/)
private transient List<Creneaux> creneauxList;
}
and
#Entity
#Table(name = "creneaux")
public class Creneaux implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Long id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ID_MEDECIN")
private transient Medecins medecin;
#Column(name = "ID_MEDECIN")
private BigInteger idMedecin;
I begin in JPA, so I a not sure about all the code. I think the Query is correct, but I don't know how to annotate the entities to make the Query valid.
Thanks

I wonder why adding the last attribute in the second Entity that is: idMedecin while you've already joined the two entities to each others. Maybe you should omit it.
If the purpose of the query is to select all the creneaux related to the given medecin, then you ought to change the query to:
createQuery("SELECT c FROM Creneaux c WHERE c.medecin.id = :idMedecin").setParameter("idMedecin", medecin.getId());
Where medecin.getId() should be provided before the query's execution.

That's because the Creneaux.medecin field is transient. Transient fields are ignored by JPA.
Another thing is that you don't have two join these two entities. If you want to filter by Medecin ID it's enough to execute a query as #Omar described.

Related

Relation between Entity and Object from service

I'm trying to make a relation between my Book entity and a list of languages that I retrieve through a service.
In my database, each book has a: ID, TITLE, CATEGORY_ID (FK), LANG_ID
Book.java:
#Entity
#Table(schema = Constants.SHEMA, name = "Book")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "TITLE")
private String title;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "CATEGORY_ID")
private Category category;
private Language language; // -> The Column associated in the database is Long LANG_ID
}
Category.java:
#Entity
#Table(schema = Constants.SHEMA, name = "Category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
}
Language.java:
public class Language implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
}
I understood the relation between Book & Category as both of them are tables in my database. However, Language is something that I get from a service and isn't persisted in my database.
The languages I get are just an ID and a Name for the language.
My question is: In order to link the language ID to my LANG_ID (the ID of the language in my Book table), what annotation (ManyToOne, Entity, ...) should I write for Language? Should I also put it in my persistence.xml ? I tried a couple but it seems like it's not working well.
Thank you very much
I don't think it is good practice to mix persisted data with non-persisted data as it can cause other unexpected problems. Anyway you can try something like this:
#Entity
#Table(schema = Constants.SHEMA, name = "Book")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "TITLE")
private String title;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "CATEGORY_ID")
private Category category;
#Column(name = "LANG_ID")
private Integer langId;
#Transient
private Language language;
#PostLoad
public void loadLanguage() {
// get the language data here
}
}
The language field has no database table, so you cannot use any mapping annotation. From the Java EE docs:
public #interface Transient
Specifies that the property or field is not persistent. It is used to annotate a property or field of an entity class, mapped superclass, or embeddable class.
Example:
#Entity
public class Employee {
#Id int id;
#Transient User currentUser;
...
}
The #PostLoad annotation declares a method to be called after the entity is loaded:
public #interface PostLoad
Specifies a callback method for the corresponding lifecycle event. This annotation may be applied to methods of an entity class, a mapped superclass, or a callback listener class.
First of all, did you consider to store language in your database? I mean language are mostly the same, doesn't change too often, you can also store in a properties file and read them at runtime to use them later.
Anyway, I think you should:
first get from external system languages
store in variable / in memory cache ( like a Map<Long,String> where you can store id and name )
read your data from database
for each row you do
read book language id, read the cache, get out data you need
If you can't change model, just use a dto with your entity and the language and you're fine

combine joined columns of a table with one column of another table into JAVA object

I want to combine column of different tables in one entity (object) but im getting a column null even it's not null.
I have two entities called Operation and CanceledOperation:
Operation:
#Entity
#Table(name = "OPERATIONS")
public class Operation{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#Column(name = "Date")
private Timestamp date;
#Transient
private String message;
// Other attributes and getters & setters
}
CanceledOperation:
#Entity
#Table(name = "CANCELED_OPERATIONS")
public class CanceledOperation{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "OPERATION_ID", nullable = false)
private Operation operation;
#Column(name = "RAINSON")
private String raison;
//getters & setters
}
I'm trying to join the operation and canceledIperation in order to display the raison for the canceled operation and null non canceled.
this is my native query in my repository:
#query(value = "SELECT op.* , co.raison as raison FROM operations op LEFT JOIN canceled_operations co ON op.ID = co.OPERATION_ID", countQuery = "SELECT count(*) FROM operations op LEFT JOIN canceled_operations co ON op.ID = co.OPERATION_ID")
Page<Operation> getAllOperations(Pageable pageable);
and this is how i call my repository:
final Page<Operation> operations= operationRepository.getAllOperations(pageable);
I tried many solution namely using the Object[] instead of Operation and I have also added an attribute called raison in the operation entity with #transient annotation, but even that I still get the raison is null.
can you please give me a hint to solve this issue.
thank in advance.
Well I found an easy way to do it, instead of doing a complicated query, I have used findAll() of each table then I affect the raison attribute from CanceledOperation to the message attribute of Operation using stream and map the two received lists from repositories.
In the CanceledOperation entity, can you give referencedColumnName = "ID" and try?
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "OPERATION_ID",referencedColumnName = "ID", nullable = false)
private Operation operation;

How to get data from joined table with where clause in JPA?

i have an Entity with following mappings:
#Entity
#Table(name = "template_product")
public class TemplateProductBean implements Serializable {
private static final long serialVersionUID = -1821696115330320798L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "product_id")
private Long productId;
// bi-directional many-to-one association to
// Template
#ManyToOne
#JoinColumn(name = "template_id")
private TemplateBean template;
The Template Bean looks as following:
#Entity
#Table(name = "template")
public class TemplateBean implements Serializable {
private static final long serialVersionUID = 3752018564161042623L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "platform_id")
private Long platformId;
#Column(name = "template_name")
private String templateName;
// bi-directional many-to-one association to
// TemplateProduct
#OneToMany(mappedBy = "template")
private List<TemplateProductBean > templateProductBean;
The Problem im facing is, im very untrained when it comes down to use the JPA Interfaces. How can i use these to get the following query:
select template
from Template template
join TemplateProductBean templateProducts
on template.templateId = templateProducts.template.templateId
where templateProducts.productId in :templateProductIdList
How can i use the JPA Interfaces( javax.persistence.criteria.Root, javax.persistence.Join etc.) to build this query as a Predicate? I'm sorry if im being unclear, i have to use this and im not used to using the JPA. Thanks
I think this should work
select t from TemplateBean t inner join t.templateProductBean tpb where tpb.productId in :templateProductIdList
More details on inner joins and HQL in general: documentation

JPQL DELETE query: delete entities within a list of another entity

i try to write a JPQL query, which deletes all entities which are within a collection of another entity.
(Example code, without getter/setter and annotations)
class Aa implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String value;
}
#Entity
class A implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JoinColumn(nullable = false)
#OneToOne
private Aa aa;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
private List<B> data;
}
#Entity
class B implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String value;
}
I have tried the following:
DELETE FROM B b WHERE b.id IN(SELECT k.id FROM A a JOIN a.data k WHERE a.id = :id)
But its end in a foreign key violation exception.
Another approach was
DELETE FROM B b WHERE EXISTS(SELECT a FROM A a WHERE a.id = :id)
but its end in a foreign key violation too.
However if i execute a sql query on the database directly, like
DELETE FROM B WHERE id = <a id number here>
then no error occurs...
EntityManager.remove() is not an option because i want to delete a huge amout of data.
I'm thankfully for every answer and help.
Can't add comments since I don't have enough reputation.
You skipped the annotations, which are kind of important, because it would help to know which side, parent, child or maybe something in between them, is in control of the relationship. For example, if parent side is controlling, in some cases it is enough to empty the List on parent side and persist to remove its children. So more code would be helpful.
EDIT 1:
If you are using JPA 2.0 you could add orphanRemoval="true" to your #OneToMany. Then just get the parent from persistence, do parent.getData().clear(), and then do EntityManager.merge(parent) OR make children aware of relationship. You are trying to remove children that aren't aware of their parent so I suggest doing so through parent, or making them aware.
Okay, with the help of Syfors' suggestions i have change the relationship between A and B:
class Aa implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String value;
}
#Entity
class A implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JoinColumn(nullable = false)
#OneToOne
private Aa aa;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, mappedBy = "owner")
private List<B> data;
}
#Entity
class B implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne
private A owner;
private String value;
}
I have also found:
[...]Normally it is best to define the ManyToOne back reference in Java[...]
Source: http://en.wikibooks.org/wiki/Java_Persistence/OneToMany
Now JPA doesn't generate a thrid table for the m:1 relationship and now my JPQL query works fine:
DELETE FROM B b WHERE b.id IN(SELECT k.id FROM A a JOIN a.data k WHERE a.id = :id)

Explicitly changing the name of a column with Hibernate

How can you rename a field name in a table which generates Hibernate?
When you create:
#ManyToMany(targetEntity = GroupRightEntity.class)
or
#ManyToMany(targetEntity = UserRightEntity.class)
Now made with an explicit change of names of columns in the Database.
alter table security_mapping_user rename column sec_mapping_id to secmappingentity_id;
Thank you for your help and understanding.
Hibernate generates links for additional tables. Accordingly, I understand that the generated field names. I need to use annotations, or something else, these column names change. I hope I wrote everything correctly.
We have.
#Entity
#Table(name = "ROLE")
public class RoleEntity implements Role, Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "SEQ_ROLE")
#SequenceGenerator(name = "SEQ_ROLE", sequenceName="SEQ_ROLE", allocationSize = 1)
private Long id;
private String name;
private String description;
#Entity
#Table(name = "URL")
public class UrlEntity implements Url, Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "SEQ_URL")
#SequenceGenerator(name="SEQ_URL", sequenceName="SEQ_URL", allocationSize = 1)
private Long id;
private String url;
#OneToMany(targetEntity = RoleEntity.class, fetch = FetchType.EAGER, mappedBy="url_id")
private Set<Role> roles;
Later we get a third table. "URL_ROLE"
In this table, the fields are named.
"URL_ID" and "ROLE_ID"
"URL_ID" need to rename the field in the "urlentity_id".
It seems now I built everything correctly.
When defining a ManyToMany you should also define which column your relationship maps on. That's where you should change the name.

Categories

Resources