I want to write a query like SELECT * FROM Release_date_type a LEFT JOIN cache_media b on a.id=b.id. I am new to Spring Data JPA. I don't know how to write entities for Join query. Here is an attempt:
#Entity
#Table(name = "Release_date_type")
public class ReleaseDateType {
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
private Integer release_date_type_id;
// ...
#Column(nullable = true)
private Integer media_Id;
// with getters and setters...
}
Another entity is:
#Entity
#Table(name = "Cache_Media")
public class CacheMedia {
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
private Integer id;
// ...
private Date loadDate; //with the getter and setter ..
}
I want to write a crudRepository interface such as
public interface ReleaseDateTypeRepository extends CrudRepository<ReleaseDateType, Long>{
#Query("SELECT * FROM Release_date_type a LEFT JOIN cache_media b on a.id=b.id")
public List<ReleaseDateType> FindAllWithDescriptionQuery();
}
For a typical example of employees owning one or more phones, see this wikibook section.
For your specific example, if you want to do a one-to-one relationship, you should change the next code in ReleaseDateType model:
#Column(nullable = true)
private Integer media_Id;
for:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="CACHE_MEDIA_ID", nullable=true)
private CacheMedia cacheMedia ;
and in CacheMedia model you need to add:
#OneToOne(cascade=ALL, mappedBy="ReleaseDateType")
private ReleaseDateType releaseDateType;
then in your repository you should replace:
#Query("Select * from A a left join B b on a.id=b.id")
public List<ReleaseDateType> FindAllWithDescriptionQuery();
by:
//In this case a query annotation is not need since spring constructs the query from the method name
public List<ReleaseDateType> findByCacheMedia_Id(Integer id);
or by:
#Query("FROM ReleaseDateType AS rdt WHERE cm.rdt.cacheMedia.id = ?1") //This is using a named query method
public List<ReleaseDateType> FindAllWithDescriptionQuery(Integer id);
Or if you prefer to do a #OneToMany and #ManyToOne relation, you should change the next code in ReleaseDateType model:
#Column(nullable = true)
private Integer media_Id;
for:
#OneToMany(cascade=ALL, mappedBy="ReleaseDateType")
private List<CacheMedia> cacheMedias ;
and in CacheMedia model you need to add:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="RELEASE_DATE_TYPE_ID", nullable=true)
private ReleaseDateType releaseDateType;
then in your repository you should replace:
#Query("Select * from A a left join B b on a.id=b.id")
public List<ReleaseDateType> FindAllWithDescriptionQuery();
by:
//In this case a query annotation is not need since spring constructs the query from the method name
public List<ReleaseDateType> findByCacheMedias_Id(Integer id);
or by:
#Query("FROM ReleaseDateType AS rdt LEFT JOIN rdt.cacheMedias AS cm WHERE cm.id = ?1") //This is using a named query method
public List<ReleaseDateType> FindAllWithDescriptionQuery(Integer id);
#Query("SELECT rd FROM ReleaseDateType rd, CacheMedia cm WHERE ...")
This has been an old question but solution is very simple to that. If you are ever unsure about how to write criterias, joins etc in hibernate then best way is using native queries. This doesn't slow the performance and very useful. Eq. below
#Query(nativeQuery = true, value = "your sql query")
returnTypeOfMethod methodName(arg1, arg2);
Related
I have Order entity which has a many-to-one relationship with Customer entity. I want to write a Spring Data JPA query to fetch all the orders that belong to the customer id.
Below is my Order entity
#Data
#NoArgsConstructor
#Builder
#AllArgsConstructor
#EqualsAndHashCode(exclude = "customer")
#ToString(exclude = "customer")
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private double price;
private LocalDate date;
#ManyToOne
#JoinColumn(name="customer_id", nullable = false)
#JsonBackReference
private Customer customer;
...
Spring Data JPA
#Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
#Query("select orders from Order where orders.customer.id = ?#{principal.id}")
Page<Order> findAll(Pageable pageable);
}
Where am I going wrong? Also can we write the above query using fluent-style based on the method naming convention in Spring Data JPA.
This HQL query should work:
#Query("select o from Order o inner join o.customer c where c.id = ?#{principal.id}")
The query based on the method name doesn't seem to support join operations, or at least I couldn't find the keyword. You can check the documentation for the list of supported keywords.
I have a couple of related entities in a relational DB:
#Entity
public class A {
#Id
#Column(name = "id" ...)
private UUID id;
#OneToMany(mappedBy = "a")
private Set<B> bs;
}
and
#Entity
public class B {
#Id
#Column(name = "id" ...)
private UUID id;
#ManyToOne
#JoinColumn(name = "a_id")
private A a;
}
So I have a B Repository
#Repository
public interface BRepository extends JpaRepository<B, UUID> {
List<B> findByA(A a);
}
And id like to have a query by which I obtain the all Bs with the same A. I'm used to have an a_id column in B, not the whole entity. I don't know how to manage this, nor with a JPA query nor with a NamedQuery.
Many thanks in advance!
Luis
As already answered, the proposed code should already work but it didn't... The fix that worked for me was
List<B> findByA_Id(UUID id);
Table_Field references field field to table Table.
This is my events entity.
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Events {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long eventId;
#NotBlank(message = "Please Add Event name ")
#Length(max =100 ,min =2)
private String eventName ;
private String eventDescription;
// Each event is going to be mapped to a Location
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(
name = "location_id",
referencedColumnName = "locationId"
)
#NotNull
private Location location ;
#Temporal(TemporalType.DATE)
Date eventStartDate;
#Temporal(TemporalType.DATE)
Date eventEndDate;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(
name = "category_id",
referencedColumnName = "categoryId"
)
#NotNull
private Categories categories;
}
In my controller ,i have access to locationId and categoryId as request params .
I am not getting how to define my eventsRepository to access by locationId and categoryId. What changes should i make to this repo for things to work .
#Repository
public interface EventsRepository extends JpaRepository<Events,Long> {
public Events findByCateoryAndLocation()
}
I think a few adjustments you need to get rid of the issue. The query builder uses actual column names, so if your column name is locationId, then use 'findByLocationId(Integer locationId)' as a prototype. And please make sure entity names suit table names.
#Repository
public interface EventRepository extends JpaRepository<Event, Integer>
{
Event findByLocationIdAndCategoryId(Integer locationId, Integer categoryId);
}
This is off-topic, but I would like to mention that please do not use Lombok in entity classes. Getter, setter, and construction generators are ok, but hascode and string generators would be dangerous if you use lazy initialization. You may not get benefits from lazy loadings.
You have 2 ways to get your jpa-query working:
Modify your JPA-Query:
#Repository
public interface EventsRepository extends JpaRepository<Events,Long>
{
public Events findByCateories_IdAndLocation_id(Long categoriesId, long locationId)
}
Use a custom query - annotate your jpa with #Query and use a native query
There is one additional point from my side.
Naming of your classes. You are using plural which conflicts with the business logic - especially to the DB-relations(see Events to Categories). I would use singular (Event, Category)
This is exactly what I did to solve this with the help of native query.
#Query(
value = "SELECT * FROM events where category_id = ?1 AND location_id = ?2",
nativeQuery = true
)
public List<Events> findByCategoryIdAndLocationIdIn(Long CategoryId , Long LocationId);
I'm facing a problem when I try to get an list of ServiceCup with its ServiceLanguage. When I try to manipulate the list of ServiceCup in my service layer hibernate is executing a second query and populate my ServiceCup with all ServiceLanguage again.
ServiceCup x ServiceLanguage x LanguageCup
ServiceCup:
#Data
#Entity
#Table(name = "csm_service")
public class ServiceCup extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private String context;
// bi-directional many-to-one association to CsmServiceLanguage
#OneToMany(mappedBy = "service")
private List<ServiceLanguage> serviceLanguages;
}
ServiceLanguage:
#Data
#Entity
#Table(name = "csm_service_language")
public class ServiceLanguage extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "translated_name")
private String translatedName;
// bi-directional many-to-one association to CsmLanguage
#ManyToOne
#JoinColumn(name = "csm_language_id_fk")
private LanguageCup language;
// bi-directional many-to-one association to CsmService
#ManyToOne
#JoinColumn(name = "csm_service_id_fk")
private ServiceCup service;
}
JpaRepository:
#Query(value = "select s, sl from ServiceCup s \n" + "INNER JOIN FETCH ServiceLanguage sl on s.id = sl.service \n"
+ "where sl.language.id = :languageId")
List<ServiceCup> findAllServicesByLanguageId(#Param("languageId") String languageId);
Query in repository layer:
select *all_fields* from csm_service servicecup0_ inner join csm_service_language servicelan1_ on (servicecup0_.id=servicelan1_.csm_service_id_fk) where servicelan1_.csm_language_id_fk=?
But in service layer execute a lot of queries to bring all the relations of ServiceCup. I want the ServiceCup objects populate but only with the results that are in the query.
How can I get a ServiceCup object with only the results of the query?
PS: In my method in service layer I have #Transactional(readOnly = true) but if I remove I can't get the objects related to ServiceCup.
I needed to use projection and not the entity to execute the query only once.
ServiceCupProj
public interface ServiceCupProj {
public Long getId();
public String getDescription();
public String getInternalname();
......
Repository
#Repository
public interface CupServiceRepository extends JpaRepository<ServiceCup, Long> {
#Query(value = "select servicecup0_.id as id, servicelan1_.translated_description as description, servicelan1_.translated_name as internalname \n"
+ "from csm_service servicecup0_ inner join csm_service_language servicelan1_ on (servicecup0_.id=servicelan1_.csm_service_id_fk) \n"
+ "where servicelan1_.csm_language_id_fk = :languageId ", nativeQuery = true)
List<ServiceCupProj> findAllServicesByLanguageId(#Param("languageId") String languageId);
......
And in the service layer I transform this ServiceCupProj in the entity that I need.
I have a similar case as this question [a link] (How do I do with HQL, many to many?)
I want to have the number of users (entity 1) for each libelle of role (entity 2). I defined a relation many to many between User and Role.
I am using Spring MVC, Hibernate, MySQL and JPA.
Entity 1: User
#Entity(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
#ManyToMany(mappedBy = "user")
private List<Role> role;
Entity 2: Role
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int ID;
private String libelle;
#ManyToMany
#JoinTable(
name = "user_role",
joinColumns = { #JoinColumn(name = "ID_role") },
inverseJoinColumns = { #JoinColumn(name = "id_user") }
)
private List<User> user;
JPA repository
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "select new map( count(u.id) as numberOfUsers,r.libelle as roleLibelle ) FROM user u join role r where r.ID =: ???? group by r.libelle")
List<Object> countByRoleList();
I am trying to figure out what =:id proposed in the question that i mentioned, have to be in my case. Instead of the "????" i tried ID, id , ID_role. All what i get is the error
"Named parameter not bound : ".
How can i solve that?
I suppose the type of your parameter is long. it's name is abc and it refer to an id.
Follow these steps:
Remove the space in front of your param .
so you will have (=:abc) not (=: abc).
Your query depends on an external parameter, uses the #param annotation for the specified param.
so you will have
".....countByRoleList(#Param("abc") long id );"
A code exemple
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
#Query(value = "select new map( count(u.id) as numberOfUsers,r.libelle as roleLibelle ) FROM user u join role r where r.ID =:abc group by r.libelle")
List<Object> countByRoleList(#Param("abc") long id);
Note: abc is a provided param . It can come from an HTML page, a function. you can also provide it manually ...