SQL aggregation GROUP BY and COUNT in Spring JPA - java

I have an SQL table:
#Table(name = "population_table")
public class Population {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String country;
private String state;
private String area;
private String population;
}
I want to get a count, grouping by country and state with the output class being List of Count:
private static class Count {
private String country;
private String state;
private long count;
}
I know the query is
SELECT country, state, Count(*)
FROM population_table
GROUP BY country, state
But I want to do this using JPA Specification. How can I achieve this using JPA Specification in spring boot?

You could achieve this by using Spring Data JPA Projections in Spring Data JPA.
Create a custom Repository method like
#Repository
public interface PopulationRepository extends JpaRepository<Population, Long> {
#Query("select new com.example.Count(country, state, count(p) )
from Population p
group by p.country, p.state")
public List<Count> getCountByCountryAndState();
}
Also you must define the specific constructor in Count class which will handle this projection
private static class Count {
private String country;
private String state;
private long count;
//This constructor will be used by Spring Data JPA
//for creating this class instances as per result set
public Count(String country,String state, long count){
this.country = country;
this.state = state;
this.count = count;
}
}

You can use JpaRepository interface.
Example:
#Repository
public interface PopulationRepository extends JpaRepository<Population, Long> {
public int countAllByCountryAndState(String countryName, String stateName);
}
And in your service:
#Service
#Transactional
public class PopulationService {
#Autowired
private PopulationRepository populationRepository;
public int countPopulationByCountryAndState(String countryName, String stateName) {
return populationRepository.countAllByCountryAndState(countryName, stateName);
}
}
Sorry, I made mistake it can be simpler. I edited my code.

Related

Hibernate is not using the proxy of a session scope bean spring

I'm using Spring #Scope(value = "session", proxyMode=ScopedProxyMode.TARGET_CLASS) beans for objects that should be shared across a single Http-Session. This will provide for example one "Project" object for each User who is using my application.
To get this working I had to implement an interceptor for Hibernate that is returning the name of the class:
public class EntityProxySupportHibernateInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = 7470168733867103334L;
#Override
public String getEntityName(Object object) {
return AopUtils.getTargetClass(object).getName();
}
}
With this interceptor I can use a Spring CrudRepository to save a Project-entity in the database:
#Repository
public interface ProjectRepository extends CrudRepository<Project, Integer> {
Project findByProjectId(int projectId);
}
Project-entity:
#Component
#Entity
#Table(name = "xxx.projects")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class Project implements Serializable {
private static final long serialVersionUID = -8071542032564334337L;
private int projectId;
private int projectType;
#Id
#Column(name = "project_id")
public int getProjectId() {
return projectId;
}
public void setProjectId(int projectId) {
this.projectId = projectId;
}
#Column(name = "project_type")
public int getProjectType() {
return projectType;
}
public void setProjectType(int projectType) {
this.projectType = projectType;
}
}
Storing the Project in the database works as expected. I can have a look at the database and the correct values are inserted. Now I have a different entity that I'm creating the same way as the project and that I want to save in the database via a CrudRepository.
Here the problem begins. Hibernate is not inserting the values that I have set. Hibernate always only inserts null into the database. Reading the values in my Spring application is working as expected. I think that Hibernate is not using the proxy of the entity but the underlying blueprint of the object. How can I force Hibernate to use the proxy with the correct values?
Repository:
#Repository("DataInput001Repository")
public interface DataInputRepository extends CrudRepository<DataInput, DataInputId> {}
Entity:
#Component("DataInput001")
#Entity
#Table(name = "xx.data_input_001")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#IdClass(DatanputId.class)
public class DataInput implements Serializable {
private static final long serialVersionUID = -6941087210396795612L;
#Id
#Column(name = "project_id")
private int projectId;
#Column(name = "income")
private String income;
#Column(name = "income_increase")
private String incomeIncrease;
/* Getter + Setter */
}
Service:
#Service("DataInputService001")
public class DataInputServiceImpl implements DataInputService {
#Resource(name = "DataInputMapper001")
DataInputMapperImpl dataInputMapper;
#Resource(name = "DataInput001Repository")
DataInputRepository dataInputRepository;
#Resource(name = "DataInput001")
DataInput datanInput;
#Transactional
public void createDataInput(String json) throws Exception {
dataInputMapper.mapDataInput(json);
dataInputRepository.save(dataInput);
}
public DataInput getDataInput() {
return dataInput;
}
public void setDataInput(DataInput dataInput) {
this.dataInput = dataInput;
}
}

How can I sort List<Object> CRUD Repository

I'm trying to build simple REST for purchases I need 2 methods. The first method should show all purchases sorted by date. The second one removes all purchases for specified date I made a method to add and to get all purchases. Now I'm stuck.
#Entity
#Table (name="purchase")
public class Purchase {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#CreationTimestamp
#Temporal(TemporalType.DATE)
#Column(name="createat")
private Date created;
#Column(name="price")
private BigDecimal price;
#Column(name="currency")
private String currency;
#Repository
public interface PurchaseRepository extends JpaRepository<Purchase, Long>
{
}
#Service
public class PurchaseService {
#Autowired
private PurchaseRepository purchaseRepository;
public void addPurchase(Purchase purchase) {
purchaseRepository.save(purchase);
}
public List<Purchase> getAllPurchase() {
List<Purchase> purchase = new ArrayList<>();
purchaseRepository.findAll().forEach(purchase::add);
return purchase;
}
}
#RestController
public class PurchaseController {
#Autowired
private PurchaseService purchaseService;
#PostMapping("/purchase")
public void addPurchase(#RequestBody Purchase purchase) {
purchaseService.addPurchase(purchase);
}
#RequestMapping("/purchase")
public List<Purchase> getAllTopics() {
return purchaseService.getAllPurchase();
}
}
What I need:
1. method to sort my List sorted by date
2. method that removes all purchases for specified date
You can use Spring Data JPA features in these cases.
Add the following methods to PurchaseRepository:
List<Purchase> findAllByOrderByCreatedAsc();
long deleteByCreated(Date created);
And after all, Spring is going to generate an appropriate query based on a method name.
I got it
long deleteByCreated(Date date);
#Transactional
public long deleteAllByDate(Date date){
return purchaseRepository.deleteByCreated(date);
}
#RequestMapping(method=RequestMethod.DELETE, value="/purchasess/{date}")
public long findAllByCreatedBetween(#DateTimeFormat(pattern="yyyy-MM-dd")
#PathVariable Date date){
return purchaseService.deleteAllByDate(date);
}

Using SQL-IN-clause in custom #Query in JPA-/CrudRepository with the list or set as passed parameter?

Hi Spring and Hibernate experts!
Can any one say if it is possible to use SQL IN-clause in custom #Query in CrudRepository while the Arraylist or set of strings is passed as parameter?
I am relatively new to Spring and do not quite figure out why I get the following Spring error:
"java.lang.IllegalArgumentException: Parameter value [d9a873ed-3f15-4af5-ab1b-9486017e5611] did not match expected type [IoTlite.model.Device (n/a)]"
In this post (JPQL IN clause: Java-Arrays (or Lists, Sets...)?) the subject is discussed pretty closely but I cannot make the suggested solution to work in my case with custom #Query.
My demo repository as part of the spring boot restful application is the following:
#Repository
public interface DeviceRepository extends JpaRepository<Device, Long> {
#Query("SELECT d FROM Device d WHERE d IN (:uuid)")
List<Device> fetchUuids(#Param("uuid") Set<String> uuid);
}
And the model-class is the following:
#Entity
#SequenceGenerator(sequenceName = "device_seq", name = "device_seq_gen", allocationSize = 1)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Device implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "device_seq_gen")
#JsonIgnore
private Integer id;
#Column(unique=true, length=36)
#NotNull
private String uuid = UUID.randomUUID().toString();
#Column(name="name")
private String name;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String description;
#OneToMany(
mappedBy="device",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Sensor> sensors = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#JsonIgnore
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeviceUuid() {
return uuid;
}
public void setDeviceUuid(String deviceUuid) {
this.uuid = deviceUuid;
}
public List<Sensor> getSensors() {
return sensors;
}
public void addSensor(Sensor sensor){
sensor.setDevice(this);
sensors.add(sensor);
}
}
An here is the relevant part of the service calling the fetchUuids-custom-method with set-list of strings as parameter (service naturally being called by the relevant restcontroller):
#Service
public class DeviceService implements IDeviceService {
#Autowired
private DeviceRepository deviceRepository;
...
#Override
public List<Device> listDevices(Set<String> clientIds) {
return deviceRepository.fetchUuids(clientIds);
}
...
}
Quick fix
You have WHERE d IN (:uuid) in the custom query. You cannot match d, which is an alias for Device entity with :uuid parameter, which is a collection of Strings.
WHERE d.uuid IN (:uuid) would fix the query - it matches a String with Strings.
What you should do instead
It's rather misleading to name the method fetchUuids and return a list of Device instances. It's also unnecessary to write a custom query to do that. You can benefor from repository method name conventions and let Spring Data Jpa framework generate the query for you:
List<Device> findByUuidIn(Set<String> uuids);
You can write in this way
#Query(value = "select name from teams where name in :names", nativeQuery = true)
List<String> getNames(#Param("names") String[] names);
and call the function in service and pass an array of String as arguments.like this
String[] names = {"testing team","development team"};
List<String> teamtest = teamRepository.getNames(names);
Yes is possible to using collection in JPA query parameters.
Your query is wrong, it should be like this:
#Query("SELECT d FROM Device d WHERE d.uuid IN :uuid")

JPA: Pass default parameter into findAll() method

I have several entities and jpa repositories to them. It looks like:
Event:
public class Event{
#Column
private String name;
#Column
private String description;
}
Place:
public class Place{
#Column
private String name;
#Column
private String description;
#Column
private Double lon;
#Column
private Double lat;
}
And repositories to them:
public interface EventRepository extends JpaRepository<Event, String>, JpaSpecificationExecutor<Event> {
}
public interface PlaceRepository extends JpaRepository<Place, String>, JpaSpecificationExecutor<Place> {
}
It work well. But then I added one else field in each entity calls tenantId
Event:
public class Event{
#Column
private String tenantId;
#Column
private String name;
#Column
private String description;
}
Place:
public class Place{
#Column
private String tenantId;
#Column
private String name;
#Column
private String description;
#Column
private Double lon;
#Column
private Double lat;
}
But all my service works with method findAll(). So, the question is:
How can I get from "old" method findAll() entities only with tenantId = "1" or "2", doesnt matter? It should be work like findAllByTenantId(String tenantId) but it should be 'findAll()'. Can I inject somehow into 'findAll()' tenantId params?
Thx.
Try to override the implementation :
public interface EventRepository extends JpaRepository<Event, String>, JpaSpecificationExecutor<Event> {
List<Event> findAll(String tenantId); // findAllByTenantId
}
JPA criteria API translates into the following query:
select e from Event e where e.tenantId = ?1
And
public interface PlaceRepository extends JpaRepository<Place, String>, JpaSpecificationExecutor<Place> {
List<Place> findAll(String tenantId); // findAllByTenantId
}
JPA criteria API translates into the following query:
select p from Place p where p.tenantId = ?1

Issue With cache Abstraction using redis

I have a Problem with Spring - Data - Redis
I am using redis as cache for entities that i am fetching from database using JPA Repository.
Here is the entity structure :
#Entity(name = "issue_category")
public class IssueCategory extends AuditablePersistable {
private static final long serialVersionUID = 8659360844089652714L;
private Integer parentId;
private String name;
private String displayNameCRM;
private String displayNameSelfServe;
private List<IssueCategoryRule> issueCategoryRules = new ArrayList<>();
public static final Integer ROOT_PARENT_ID = 0;
// with all getter setters
}
Here is part where i am caching it:
#Override
#Cacheable(value = IC_CACHE, key = "#id")
public IssueCategory getIssueCategoriesById(Integer id) {
return issueCategoriesRepo.findById(id);
}
The Repo code for getting from db:
public interface IssueCategoryRepo extends JpaRepository<IssueCategory, Integer>
{
public IssueCategory findByName(#Param("name") String name);
public List<IssueCategory> findByParentId(#Param("parentId") Integer parentId);
public IssueCategory findById(#Param("id") Integer id);
}
The problem I am getting is
Class Cast exception when this entity is being extracted from cache
It says :
Can not cast from java.util.ArrayList to IssueCategory class.
Can anybody help.

Categories

Resources