I'm trying to get into functional programming on java 8
User class
public class User {
private String name;
private Country country;
private int age;
// setters - getters
}
Country class
public class Country {
private String name;
private int population;
//setters - getters
}
Now I want to get the average age of Users by Country, basically a Map<String, Double>. I can do it the old way just by iterating the users list. However I would like to implement it in a functional way , with stream. I know about collect and groupingBy.
I tried users.stream().collect(Collectors.groupingBy(user -> user.getCountry())); and get a Map<String, List<User>>. Can't figure out how to do the averaging part.
Add a second parameter to groupingBy
Map<String, Double> map = users
.stream()
.collect(Collectors.groupingBy(
user -> user.getCountry().getName(),
Collectors.averagingInt(User::getAge)));
Related
I'm trying to retrieve a list of products by one of their variants ids but I simply couldn't figure it out with nested classes and a list.
I've tried the method
List<Product> findAllByFooProductVariantsCodeIn(List<String> variantCodes);
It didn't work out as well.
public class Product extends DatabaseEntity {
private FooProduct fooProduct;
}
public class FooProduct {
private String title;
private String vendor;
private String productType;
private String tags;
private List<FooProductVariant> variants;
}
public class FooProductVariant {
private String productId;
private String title;
private String price;
private String variantCode;
}
An example of this could be 3 base Products with 3 variants
1st product ABC
2nd product RTY
3rd product XYZ
and the variantCodes of these products:
ABC-003,
RTY-001,
XYZ-002
the parameter variantCodes does contain all these variant codes and the method is expected to return 3 products as ABC, RTY, XYZ. Query needs to get into the nested FooProduct and after the FooProductVariant list inside it, and unnest the variant codes and find the Product from one of these variant Codes
Is this doable with Spring Data JPA?
I am using java+Spring framework+Hibernate for creating rest api but I have stumbled upon retrieving details of a table using foreign key attributes.
I have the following tables::
https://i.stack.imgur.com/lG7UR.png
I am retrieving all the ratings given using product id and then mapping to DTO, now I also want to populate the username using idusers as this is my foreign key.
Same is the case when I try to retrieve ratings given by the users, instead of displaying idproducts I want to display the product name and product description as It is a foreign key.
Any advice on how to do so using DTO's.
This is a perfect use case for Blaze-Persistence Entity Views.
Blaze-Persistence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
Assuming you have an entity model like this
#Entity
public class User {
#Id
Integer id;
String role;
String username;
String password;
boolean enabled;
}
#Entity
public class Product {
#Id
Integer id;
String imageUrl;
String category;
int productPrice;
int productQuantity;
String productName;
String productDesc;
#OneToMany(mappedBy = "product")
Set<Rating> ratings;
}
#Entity
public class Rating {
#Id
Integer id;
int rating;
String review;
String ratingscol;
#ManyToOne(fetch = LAZY)
Product product;
#ManyToOne(fetch = LAZY)
User user;
}
A DTO mapping for your model could look as simple as the following
#EntityView(Rating.class)
interface RatingDto {
Integer getId();
UserDto getUser();
ProductDto getProduct();
}
#EntityView(User.class)
interface UserDto {
Integer getId();
String getUsername();
}
#EntityView(Rating.class)
interface ProductDto {
Integer getId();
String getProductName();
String getProductDesc();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
RatingDto dto = entityViewManager.find(entityManager, RatingDto.class, id);
But the Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
It will only fetch the mappings that you tell it to fetch
You can use ModelMapper when converting a DTO to an Entity bean and back from Entity bean to a DTO.
Add ModelMapper to your project
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.5</version>
</dependency>
Define the ModelMapper bean in your Spring configuration
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
Assuming the following models based on the given ER diagram you have given
public class UserDto {
Integer userId;
String role;
String username;
String password;
boolean enabled;
...default and parameterized constructor
...getter and setter methods
}
public class ProductDto {
Integer productId;
String imageUrl;
String category;
int productPrice;
int productQuantity;
String productName;
String productDesc;
...default and parameterized constructor
...getter and setter methods
}
public class RatingDto {
#Id
Integer id;
int rating;
String review;
String ratingscol;
ProductDto productDto;
UserDto userDto;
...default and parameterized constructor
...getter and setter methods
}
You can retrieve the ratings of a product using product id along with the user details by using the following method
#Repository
public interface RatingRepository extends JpaRepository<Rating, Integer>{
List<Rating> findByProduct_ProductId(Integer productId);
}
Then mapping rating objects to DTO
RatingDto ratingDto = modelMapper.map(rating, RatingDto.class);
Now you can retrieve username as following
ratingsDto.getUserDto().getUserName()
The same way you can retrieve the ratings by userId and access product details
I am using spring boot (version - 2.1.1). I have a one to many database model exposed for CRUD operations through rest api's. The model looks as below. How do I configure the POST /departments api (that creates a department object) to accept just the organization id in the input json body?
#PostMapping
public Long createDepartment(#RequestBody Department Department) {
Department d = departmentService.save(Department);
return d.getId();
}
Note - I do not want to allow creating organization object when creating a department.
Model object mapping
#Entity
#Table(name="ORGANIZATIONS")
public class Organization{
#Id
#GeneratedValue
Private long id;
#Column(unique=true)
Private String name;
#OneToMany(mappedBy = "organization", fetch = FetchType.EAGER)
private List<Department> departments;
}
#Entity
#Table(name="DEPARTMENTS")
Public class Department{
#Id
#GeneratedValue
Private long id;
#Column(unique=true)
Private String name;
#ManyToOne(fetch = FetchType.EAGER)
private Organization organization;
}
Thanks!
The easiest and most sane way in my opinion is to utilize the DTO (Data Transfer Object) pattern.
Create a class that represent the model you want to get as your input:
public class CreateDepartmentRequest {
private long id;
// getters and setters
}
Then use it in your controller:
#PostMapping
public Long createDepartment(#RequestBody CreateDepartmentRequest request) {
Department d = new Department();
d.setId(request.getId());
Department d = departmentService.save(d);
return d.getId();
}
Side note, its better to ALWAYS return JSON through REST API (unless you use some other format across your APIs) so you can also utilize the same pattern as I mentioned above to return a proper model as a result of the POST operation or a simple Map if you don't want to create to many models.
My POJO Datasource basically contains following structure.
// Company.java
public class Company implements Serializable {
private static final long serialVersionUID = 3130918429913376956L;
private String name;
private String address;
private String contactPerson;
private String mobile;
private String fax;
private String bankDetails;
private String email;
private List<Employee> emps;
//getter and setter.
}
// Employee.java
public class Employee implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4473328670062370497L;
private String name;
private int age;
private String designation;
//getter and setter
}
My scenario is like following
One PDF report may have more that one Company (ie List< Company >)
In case of more than one Company, it should start at new page.
If Employee list goes to next page then it should repeat Header on the next page.
Layout -
Layout xml source
Output Page 1
Page 2
There are two issues with this design
Employee Name header is getting repeated for every employee.
Company Header (Comp Name -> Company A ) should be rendered only once.
Can anyone suggest me correct approach ? Thanks in advance.
Move the 'company name' to the Header row and set the header property to not repeating on new pages. Move the 'Employee name' out of the grouping with 'Employee', so up to the level where 'company name' is now.
I think you know everything to solve this, you just have to fiddle with the groupings a bit.
I have two such Java object:
public class PSubject
{
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
#org.apache.solr.client.solrj.beans.Field("name")
private String name;
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
#org.apache.solr.client.solrj.beans.Field("type")
private String type;
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
#org.apache.solr.client.solrj.beans.Field("uri")
private String uri;
#OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
#IndexedEmbedded
#org.apache.solr.client.solrj.beans.Field("attributes")
private Set<PAttribute> attributes = new HashSet<PAttribute>();
.....
}
#Entity
#Indexed
#Table(name="PAttribute")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class PAttribute extends PEntity
{
private static final long serialVersionUID = 1L;
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.YES)
#org.apache.solr.client.solrj.beans.Field("attr_name")
private String name;
#Column
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.YES)
#org.apache.solr.client.solrj.beans.Field("attr_value")
private String value;
.....
}
And my Spring Data Solr query interface:
public interface DerivedSubjectRepository extends SolrCrudRepository<PSubject, String> {
Page<PSubject> findByName(String name, Pageable page);
List<PSubject> findByNameStartingWith(String name);
Page<PSubject> findBy(Pageable page);
#Query("name:*?0* or description:*?0* or type:*?0* or mac_address:*?0* or uri:*?0* or attributes:*?0*")
Page<PSubject> find(String keyword,Pageable page);
#Query("name:*?0* or description:*?0* or type:*?0* or mac_address:*?0* or uri:*?0* or attributes:*?0*")
List<PSubject> find(String keyword);
}
I can search any by name, description, type and mac_address, but can't search any result by attribute.
Update:
For example,when user search "ipod", it's probably means the type of subject or name of subject, or the name of attribute or the value of attribute. And I want get all the matched subject in one request. I know I can search the attribute object in a separate query. But that makes the code in the backend complex.
So, how can I search this nested object?
Update:
I flattened my data:
#Transient
#Field(index=Index.YES, analyze=Analyze.YES, store=Store.NO)
#org.apache.solr.client.solrj.beans.Field("attrs")
private String attrs;
public String getAttrs() {
return attrs;
}
public void setAttrs(Set<PAttribute> attributes) {
StringBuffer attrs = new StringBuffer();
if(attributes==null) {
attributes = this.getAttributes();
}
for(PAttribute attr:attributes){
attrs.append(attr.getName()+" " + attr.getValue()).append(" ");
}
this.attrs =attrs.toString();
}
The issue is resolved.
IIRC it is not possible to store nested data structures in solr - it depends how you flatten your data to fit into an eg. multivalue field - a little hard not knowing your schema.
see: http://lucene.472066.n3.nabble.com/Possible-to-have-Solr-documents-with-deeply-nested-data-structures-i-e-hashes-within-hashes-td4004285.html
How does the data look like in you index, and did you have a look at the http request sent by spring-data-solr?