I'm trying to pull an alias from native query in JPA, something like (SUM,COUNT), Well the method can return an integer if i pulled SUM or COUNT perfectly (ONLY if i pulled it alone) .
but how can i pull it with the rest of object? here is a sample what i am trying to do
#Entity
#Table("hotels")
public class Hotel {
#Column(name="id")
#Id
private int hotelId;
#Column(name="hotel_name")
private String hotelName;
#OneToMany
private List<Availability>list;
private int avaialbeCount; //this one should be Aliased and need to be pulled by none column
}
Repository
public interface HotelRepository extends JpaRepository<Hotel,Integer>{
#Query(value="select h.*,a.count(1) as avaialbeCount from hotels h INNER JOIN availability a on (a.hotel_id=h.hotel_id) group by a.date",nativeQuery=true)
public List<Hotel> getHotels();
}
in the above repository. im trying to get avaialbeCount with hotel columns but im unable to pull it, however i can pull it by removing the select h.* and keep select COUNT only and make the method returns Integer instead of Hotel
You can use JPQL, something like this
#Query("SELECT new test.Hotel(h.hotelName, count(h)) FROM Hotel h GROUP BY h.hotelName")
to use this new test.Hotel(h.hotelName, count(h)) construction, you need constructor like
public Hotel(String hotelName, Long avaialbeCount) {
this.hotelName = hotelName;
this.avaialbeCount = avaialbeCount;
}
Example:
Repository:
package test;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface HotelRepo extends JpaRepository<Hotel, Long> {
#Query("SELECT new test.Hotel(h.hotelName, count(h)) FROM Hotel h GROUP BY h.hotelName")
List<Hotel> getHotelsGroupByName();
}
Entity:
package test;
import javax.persistence.*;
#Entity
#Table(name = "hotels")
public class Hotel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long hotelId;
#Column(name = "hotel_name")
private String hotelName;
#Transient
private Long avaialbeCount;
public Hotel() {
}
public Hotel(String hotelName) {
this.hotelName = hotelName;
}
public Hotel(String hotelName, Long avaialbeCount) {
this.hotelName = hotelName;
this.avaialbeCount = avaialbeCount;
}
#Override
public String toString() {
return "Hotel{" +
"hotelId=" + hotelId +
", hotelName='" + hotelName + '\'' +
", avaialbeCount=" + avaialbeCount +
'}';
}
}
#Transient annotation is used to indicate that a field is not to be persisted in the database.
Related
I am beginner with Spring Boot and trying to improve my skills to get new job, so I hope you help me even if the question maybe easy for you as I search a lot and gain nothing.
I need to get by id, but return data is duplicated with only one record, I will show you what I do and the result for more explanation.
In DB side:
I have VW_Prices view in DB and it's data as shown below:
In Spring Boot side:
VW_Prices class is :
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Immutable;
#Entity
#Table(name = "VW_PRICES")
public class VW_Prices implements Serializable {
private long dealId;
private Long quotationId;
private Long productPriceForEjada;
private Long productPriceForClient;
private Long productId;
private Long productQuantity;
private String productName;
#Id
#Column(name = "ID")
public long getDealId() {
return dealId;
}
public void setDealId(long dealId) {
this.dealId = dealId;
}
#Column(name = "PRODUCT_QUANTITY")
public Long getProductQuantity() {
return productQuantity;
}
public void setProductQuantity(Long productQuantity) {
this.productQuantity = productQuantity;
}
#Column(name = "PRODUCT_NAME")
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
#Column(name = "PRODUCT_PRICE_FOR_EJADA")
public Long getProductPriceForEjada() {
return productPriceForEjada;
}
public void setProductPriceForEjada(Long productPriceForEjada) {
this.productPriceForEjada = productPriceForEjada;
}
#Column(name = "PRODUCT_PRICE_FOR_CLIENT")
public Long getProductPriceForClient() {
return productPriceForClient;
}
public void setProductPriceForClient(Long productPriceForClient) {
this.productPriceForClient = productPriceForClient;
}
#Column(name = "PRODUCT_ID")
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
#Column(name = "QUOTATION_ID")
public Long getQuotationId() {
return quotationId;
}
public void setQuotationId(Long quotationId) {
this.quotationId = quotationId;
}
}
and I create VW_PricesRepository
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import springboot.deals_tracker_system.models.VW_Prices;
import springboot.deals_tracker_system.models.VW_Prices_interface;
public interface VW_PricesRepository extends JpaRepository<VW_Prices, Long> {
#Query( nativeQuery = true,value = "SELECT distinct * from VW_Prices v where v.id = :dealID " )
List<VW_Prices> findByDealId( #Param("dealID") Long id);
}
and my in my Service
public List<VW_Prices> findByDealId(Long dealId) {
System.out.println("we are in service");
List<VW_Prices> variableForDebug = VW_pricesRepository.findByDealId(dealId);
for (VW_Prices vw_Prices : variableForDebug) {
System.out.println(vw_Prices.getDealId() + " " + vw_Prices.getProductName());
}
return variableForDebug;
//return VW_pricesRepository.findByDealId(dealId);
}
When I pass dealId = 39 the result comes duplicated and not correct as in below:
how can I get correct data??
The view is made for Quotation Product Table to get product name.
i think the problem is the id annotation you must add GeneratedValue
fro the class:
#Entity
#Table(name = "VW_PRICES")
public class VW_Prices implements Serializable {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private long dealId;
private Long quotationId;
private Long productPriceForEjada;
private Long productPriceForClient;
private Long productId;
private Long productQuantity;
private String productName;
//code..
}
You dont have to use JPQL for this type of queries it's already exist in jpa:
VW_PricesRepository:
public interface VW_PricesRepository extends JpaRepository<VW_Prices, Long> {
}
to get data by id use findById like that:
public VW_Prices findByDealId(Long dealId) {
System.out.println("we are in service");
VW_Prices vw_Prices = VW_pricesRepository.findById(dealId);
System.out.println(vw_Prices.getDealId() + " " +
vw_Prices.getProductName());
}
return vw_Prices;
}
All data should be deleted from VW_Prices table because ids are not unique, try to insert new data with unique id then try the above code
I detect the problem, The view has main table Quotation and I didn't select it's ID and I used ID of the secondary table as the main ID for the View
I just write it if any one Google for such problem
I'm a beginner with spring and I have this little issue. "No property questionId found for type CourseTestCompleteField!" I have 2 model classes that are connected via a one to one join.
That 2 model class are:
package com.example.springboot.models;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
#Entity
#Table(name = "questions")
public class CourseTestQuestion {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="question_id")
private Long id;
#NotBlank
#Column(name = "question_course")
private String questionCourse;
#NotBlank
#Column(name = "question_type")
private String questionType;
public CourseTestQuestion(){
}
public CourseTestQuestion(String questionCourse, String questionType) {
this.questionCourse = questionCourse;
this.questionType = questionType;
}
// public getters and setters for all fields here
}
And:
package com.example.springboot.models;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
#Entity
#Table(name = "quiz_complete_field_questions",
uniqueConstraints = {
#UniqueConstraint(columnNames = "question_id")
}
)
public class CourseTestCompleteField {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Column(name = "question_content")
private String questionContent;
#NotBlank
#Column(name = "answer")
private String answer;
#NotBlank
#Column(name = "points")
private String points;
#NotBlank
#Column(name = "course")
private String course;
#NotBlank
#Column(name = "teacher_username")
private String teacher;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "question_id", referencedColumnName = "question_id")
private CourseTestQuestion courseTestQuestion;
public CourseTestCompleteField(){
}
public CourseTestCompleteField(CourseTestQuestion courseTestQuestion, String question, String answer, String points, String course, String teacher) {
this.courseTestQuestion = courseTestQuestion;
this.questionContent = question;
this.answer = answer;
this.points = points;
this.course = course;
this.teacher = teacher;
}
// public getters and setters for all fields here
}
My repo for both:
package com.example.springboot.repository;
import com.example.springboot.models.Course;
import com.example.springboot.models.CourseTestQuestion;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
#Repository
public interface CourseTestQuestionRepository extends JpaRepository<CourseTestQuestion, Long> {
Optional<CourseTestQuestion> findById(Long id);
Optional<CourseTestQuestion> findByQuestionCourse(String questionCourse);
}
And:
package com.example.springboot.repository;
import com.example.springboot.models.CourseTestCompleteField;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
#Repository
public interface CourseTestCompleteFieldRepository extends JpaRepository<CourseTestCompleteField, Long> {
Optional<CourseTestCompleteField> findById(Long id);
Optional<CourseTestCompleteField> findByQuestionId(Long questionId);
Optional<CourseTestCompleteField> findByCourse(String course);
List<CourseTestCompleteField> findByQuestionContent(String questionContent);
List<CourseTestCompleteField> findByTeacher(String teacher);
Boolean existsByQuestionContent(String questionContent);
}
The problem is with Optional<CourseTestCompleteField> findByQuestionId(Long questionId);but I don't get it why, because in database I have the table for CourseTestCompleteFieldModel with question_id column, and in CourseTestCompleteField I have CourseTestQuestion object. Tho, the table for CourseTestCompleteField has a different name, could be this a problem? I should rename the table to course_test_complete_field?
Can someone help me please? Thank you
Since,This is a query on nested Object. You need to update your query as this.
Optional<CourseTestCompleteField> findByCourseTestQuestion_Id(Long questionId);
This works even without "_"
Optional<CourseTestCompleteField> findByCourseTestQuestionId(Long questionId);
But better to put "_" while accessing nested fields for better readability.
There is no field call questionId in you entity and you have id only.
That's you got error. You can use that findyById(). That's only enough.
If you would like write JPA repository method like findBy..., getBy..., deleteBy...., countBy..., After this you need append exact field name from entity.
For example if you entity have name then can write below methods. findByName(); deleteByName(); countByName();
So try as below.
findBycourseTestQuestion(Object o);
Pass questions object.
I have 3 entities -
Course
Module
Timeline
Course is an independent entity with following attributes:
Course - (id Integer Primary Key, course_name)
#Id
#Column(name = "id")
Integer courseId;
#Column(name = "course_name")
String course_name;
Next up is another entity Module,
Every row in module is related to one course, and hence there is a one to one relationship between Module and Course.
Module - (module_id, module_name, module_type, duration)
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "module_id")
Integer module_id;
#Column(name = "module_name")
String module_name;
#Column(name = "duration")
Integer duration;
#ManyToOne
#JoinColumn(name="timeline_id", nullable=false)
private Timeline timeline;
Now, next is a timeline entity, which is also related to course i.e every timeline id belongs to one course id, but one timeline id can belong to multiple module_ids, and hence below code:
#Id
#Column(name = "timeline_id")
Integer timelineId;
#OneToMany( mappedBy = "timeline" )
private List<Module> module;
#OneToOne( cascade = CascadeType.ALL)
private Course course;
Can you please tell me what is the error over here.
ModuleRepository:
#Repository
public interface ModuleRepository extends JpaRepository<Module, Integer>{
public List<Module> findAllByTimelineTimelineId(Integer timelineId);
}
IModuleService
public interface IModuleService {
public List<Module> findByTimelineId(Integer timelineId);
}
ModuleServiceImpl
public List<Module> findByTimelineId(Integer timelineId) {
// TODO Auto-generated method stub
return moduleRepo.findAllByTimelineTimelineId(timelineId);
}
Controller
#RequestMapping("/gettimeline/{timeline_id}")
public List<Module> findByTimelineId(#PathVariable Integer timeline_id){
return moduleService.findByTimelineId(timeline_id);
}
Now when I run this url in Postman: http://localhost:8083/gettimeline/1
I get an infinite loop, I am unable to decode the error, also is there any problem with OneToMany mapping, I am new to JPA:
[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[
Please help, thank you in advance :)
The infinite loop issue is caused by the one-to-many relation. There are several ways of fixing this, but I find view model classes like shown below as the cleanest approach.
Please note that the owning side of the one-to-many relation is not included in the code below, only the many-to-one. This can be done the other way around, but from your code, I guess this is what you want.
TimelineVM class
package no.mycompany.myapp.misc;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class TimelineVM {
private Integer timelineId;
public TimelineVM(Timeline timeline) {
this.timelineId = timeline.getTimelineId();
}
}
ModuleVM class
package no.mycompany.myapp.misc;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class ModuleVM {
private Integer module_id;
private String module_name;
private Integer duration;
private TimelineVM timeline;
public ModuleVM(Module module) {
this.module_id = module.getModule_id();
this.module_name = module.getModule_name();
this.duration = module.getDuration();
this.timeline = new TimelineVM(module.getTimeline());
}
}
Controller method
#RequestMapping("/gettimeline/{timeline_id}")
public List<ModuleVM> findByTimelineId(#PathVariable Integer timeline_id){
return moduleService.findByTimelineId(timeline_id).stream().map(ModuleVM::new).collect(Collectors.toList());
}
I would like to load the database type ( Cassandra or MongoDB) in my Springboot Service class based on some condition. For this, I was planning to use #Conditional annotation mentioned in https://sivalabs.in/2016/03/how-springboot-autoconfiguration-magic/.
In my case, I am extending the org.springframework.stereotype. Repository Interface to create the Repository like below.
In my service class, how can I get the instance of Cassandra Type or Mongo Type based on a profile or configuration in application.yml?
Thanks
Ashish
#Repository
public interface CartCassandraRepository extends CrudRepository<com.cassandra.Cart, String> {
}
#Repository
public interface MongoCassandraRepository extends CrudRepository<com.mongo.Cart, String> {
}
Here is my com.cassandra.Cart.java
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.cassandra.core.mapping.Column;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
#Table("Cart")
public class Cart {
#PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
#GeneratedValue(strategy = GenerationType.AUTO)
protected String id;
#PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
private String userId;
#PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING)
private String productId;
}
Here is my com.mongo.Cart.java
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field
#Document(collection = "Cart")
public class Cart {
#Id
protected String id;
#Indexed(unique = true)
#Field(value = "UserId")
private String userId;
#Field(value = "ProductId")
private String productId;
}
Here's the Service class
public class CartServiceImpl{
#Autowired
CassandraCartRepository cassandraCartRepository;
#Autowired
MongoCartRepository mongoCartRepository;
public Cart save(CartDTO cart){
// based on configuration or profile load com.mongo.Cart or com.cassandra.Cart
// based on the configuration or profile do mongoCartRepository.save(cart) or cassandraCartRepository.save(cart)
}
}
Suppose I have two resources Person and Article
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long person_id;
private String firstName;
private String lastName;
#OneToMany(mappedBy="person", cascade=CascadeType.ALL)
private List<Article> articles = new ArrayList<>();
}
#Entity
#Table(name="article")
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
private String details;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id")
private Person person;
}
I now want to add HATEOAS support to the response of the controller for which I am using org.springframework.hateoas.ResourceAssembler
public class PersonResourceAssembler implements ResourceAssembler<Person, Resource<Person>> {
private EntityLinks entityLinks;
public UserJobResourceAssembler(EntityLinks entityLinks) {
this.entityLinks = entityLinks;
}
#Override
public Resource<Person> toResource(Person entity) {
Resource<UserJob> resource = new Resource<>(entity);
resource.add(
entityLinks.linkFor(Person.class).withSelfRel()),
entityLinks.linkFor(...logic...).withRel("articles")) //here I am hardcoding the relation link name i.e "article"
);
return resource;
}
}
So, in above code the "article" is hardcoded for the link name, but I don't want to do it this way. I want it do in the way Spring-Data-REST handles it i.e for every relationship it auto detects the name of the variable used inside the Entity class e.g articles will be picked from Person and person will be picked from Article.
I have no idea how Spring-Data-REST handles it, but are there any readymade/custom solutions for this requirement?
You can use the reflection API to introspect the entity and find associations. Something like:
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.OneToMany;
public class AssociationUtility {
public static List<Field> getAssociatedFields(Object entity) {
Stream<Field> fields = Arrays.stream(entity.getClass().getDeclaredFields());
return fields.filter(field -> field.getAnnotation(OneToMany.class)
!= null).collect(Collectors.toList());
}
public static void main(String[] args) {
List<Field> fields = getAssociatedFields(new Customer());
fields.stream().forEach(f -> System.out.println("Make a link for Class: "
+ ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0]
+ " with rel: " + f.getName()));
System.exit(0);
}
}