How to get an "ORDER BY" style of sorting in Java? - sql-order-by

I have a class for example Person.
public class Person {
private Integer age;
private String firstName;
private String lastName;
// getters and setters
}
I want to sort this in the ORDER BY style, which is provided by SQL queries. Collections.sort(listOfPersons, ) should return me data in this fashion.
"SELECT * FROM PERSONS ORDER BY AGE, FIRSTNAME, LASTNAME;"

Implement your own Comparator would do, like:
Collections.sort(listOfPerson, new Comparator() {
public int compare(Person p1, Person p2) {
...
}
});

Related

Create a Spring Search DTO with defined list

Is it possible to create a DTO with validation tag which accepts only defined list of values? For example:
#Getter
#Setter
public class SearchParams {
private String title;
#NotNull
private String type; // type can be only 'approved', 'new' and 'closed'
}
Is there some way to use tags to have only this strict list of values?
#Getter
#Setter
public class SearchParams {
private String title;
#NotNull
private TypeEnum type;
}
public enum TypeEnum {
APPROVED,
NEW,
CLOSED
}
You can enrich the enum with a description in order to have control on the case sensitiveness
public enum TypeEnum {
APPROVED("approved"),
NEW("new"),
CLOSED("closed");
private String desc;
TypeEnum(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
static TypeEnum fromDesc(String desc) {
TypeEnum[] values = TypeEnum.values();
for (TypeEnum typeEnum: values) {
if (typeEnum.getDesc().equals(desc)) {
return typeEnum;
}
}
return null;
}
}
So you can get the relevant enumeration via a lowercase String as:
TypeEnum.fromDesc("new") // this will return TypeEnum.NEW

JPA: How can I read particular fields of an Entity?

I use Spring JPA ( Hibernate ) and have bunch of entities which are mapped onto tables.
When I use an entity to write I need many fields in it (see an example below). But when I read, I wanna sometimes read only particular fields like first/last name. How can I perform it using Spring data JPA ? ( because due to CrudRepository nature it returns the whole entity)
#Entity
#Table(name="PERSON")
#AttributeOverride(name = "id", column = #Column(name = "ID_PERSON"))
public class Person extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="LAST_NAME", length = 100, nullable = false)
private String lastName;
#Column(name="FIRST_NAME", length = 50, nullable = false)
private String firstName;
#Column(name="MIDDLE_NAME", length = 50)
private String middleName;
#Column(name="BIRTHDAY", nullable = false)
#Temporal(value = TemporalType.DATE)
private Date birthday;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "ID_SEX")
private Sex sex;
public Person() {
super();
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Sex getSex() {
return sex;
}
public void setSex(Sex sex) {
this.sex = sex;
}
}
There are various possibilities.
With Spring Data JPA you can use projection (that's the name when you only select certain fields/columns of an entity/table).
You can return List of Object[] or a DTO or an Interface.
For example with interface it looks like this:
interface NamesOnly {
String getFirstname();
String getLastname();
}
interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastname(String lastname);
}
As you can see the return value most not be of type Person.
Please check out the documentation:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
I was faced with a similar issue and I resorted to this:
Let's say you have your entity FooEntity related to repository FooRepository
To only get certain fields, let's say firstName and lastName using key I had to create a custom query in the FooRepository
In this manner
#Query("select new FooEntity(f.firstName, f.lastName) from FooEntity f where f.key = :key")
Optional<FooEntity> findCustomByKey(#Param("key") BigInteger key);
You also have to ensure that the FooEntity has the constructor accepting the values that you only want to be set or returned in this manner:
public FooEntity(String firstName, String lastName){
// Ensure the constructor is not called with null values
notNull(firstName, "Method called with null parameter (firstName)");
notNull(lastName, "Method called with null parameter (lastName)");
this.firstName = firstName;
this.lastName = lastName;
}
Please the full code below:
public class FooEntity implements Serializable {
#Id
#Column(name = "key")
private BigInteger key;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "birth_date")
private Date birthDate;
#Column(name = "hash")
private String hash;
public FooEntity(String firstName, String lastName){
// Ensure the constructor is not called with null values
notNull(firstName, "Method called with null parameter (firstName)");
notNull(lastName, "Method called with null parameter (lastName)");
this.firstName = firstName;
this.lastName = lastName;
}
// Getters and Setters
}
public interface FooRepository extends JpaRepository<FooEntity, BigInteger>{
#Query("select new FooEntity(f.firstName, f.lastName) from FooEntity f where f.key = :key")
Optional<FooEntity> findCustomById(#Param("key") BigInteger key); // This one only returns two set fields firstName and LastName and the rest as nulls
Optional<FooEntity> findById(BigInteger key) // This one returns all the fields
}

extract less number of columns from database table as defined in #Entity class and map to same entity pojo in spring boot

My #Entity class is
#Entity
class Demo{
#Id
private int id;
private int firstName;
private String lastName;
private String address;
}
And the #Repositiory Interface is having method as below
#Query(value="select d.id,d.firstName from demo d",nativeQuery=true)
List<Demo> fetchDetails();
Here exception is thrown as : The field "lastName" is not present in ResultSet
Do i need to create another pojo that contain id,firstName as variable and change fetchDetails() methods to as below:
#Query(value="select d.id,d.firstName from demo d",nativeQuery=true)
List<New Pojo class with only 2 fields that is to be selected> fetchDetails();
i want the partially selected resultset to get mapped to Entity Demo automatically.
I their any way to map these two columns to the Entity Demo
You can use Class-Based Projections that you can have a lot of constructor you need according to all fields you want to fetch
For example, here's a projection class for the Demo entity:
public class DemoDto {
private int id;
private int firstName;
private String lastName;
private String address;
// getters, equals and hashCode
}
public DemoDto(String firstName) {
this.firstName = firstName;
}
public DemoDto(int id, String firstName) {
this.id = id;
this.firstName = firstName;
}
public DemoDto(int id, String firstName, String address) {
this.id = id;
this.firstName = firstName;
this.address = address;
}
You must also define equals and hashCode implementations – they allow Spring Data to process projection objects in a collection.
In your repository you can add some query with JPQL Constructor like:
#Query(value="select new your.class.fullname.package.DemoDto(d.firstName) from Demo d")
List<DemoDto> fetchNameOnly();
#Query(value="select new your.class.fullname.package.DemoDto(d.id, d.firstName) from Demo d")
List<DemoDto> fetchIdAndNameOnly();
#Query(value="select new your.class.fullname.package.DemoDto(d.id, d.firstName, d.address) from Demo d")
List<DemoDto> fetchAllDetails();
Projections are introduced for that exact reason. Have a look at the documentation here
What you need is this, create an interface like this with the getter method for the fields you want in the result.
interface IdAndNameOnly {
String getFirstname();
int getId();
}
Modify the query like this. You do not need #Query for simple queries like the one you have.
List<IdAndNameOnly> findAll();
You can convert object of type IdAndNameOnly to your Entity type. But that doesn't make much sense. You can just get the fields which you need from the IdAndNameOnly object. If not what is the point of fetching fewer fields.
If I'm not mistaken you need to create a custom constructor and use it JPQL Constructor Expressions.
Something like this would do the job:
#Entity
class Demo{
#Id
private int id;
private int firstName;
private String lastName;
private String address;
public Demo() {
// JPA needs the default constructor
}
public Demo(int id, String firstName) {
this.id = id;
this.firstName = firstName;
}
}
And the usage something like this:
#Query(value="select new your.class.fullname.package.Demo(d.id,d.firstName) from Demo d")
List<Demo> fetchDetails();

SpringBoot concatenate search parameters browser url

I am starting working with Spring Boot. My aim is to make a limited search retrieving data from a database. I want to add multiple parameters in the query of the url.
So far I was able using the seek: http://localhost:8080/wsr/search/, to get a full search of the data in the database. But what I want is delimit the search under several conditions adding parameters in the url in the browser as for instance:
http://localhost:8080/data/search/person?name=Will&address=Highstreet&country=UK
http://localhost:8080/data/search/person?name=Will&name=Angie
http://localhost:8080/data/search/person?name=Will&name=Angie&country=UK
The problem I found is that I can't find the way to work with more than one condition. The only thing I got to make it work, is:
http://localhost:8080/data/search/person?name=Will
I surfed the web but no results for this exact problem, too much information but impossible to find this.
The code I have is:
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
#Column(name = "country")
private String country;
public Value() {
}
public Value(int id, String name, String address, String country) {
this.id = id;
this.name = name;
this.address = address;
this.country = country;
}
//all getters and setters
}
public class Implementation {
#Autowired
private DataBase dataBase;
public List<Value> findById(#PathVariable final int id) {
return dataBase.findById(id);
}
public List<Value> findByName(#PathVariable final String name) {
return dataBase.findByName(name);
}
public List<Value> findByAddress(#PathVariable final String address) {
return dataBase.findByAddress(address);
}
public List<Value> findByCountry(#PathVariable final String country) {
return dataBase.findByCountry(country);
}
}
//#Component
#RepositoryRestResource(collectionResourceRel = "person", path = "data")
public interface DataBase extends JpaRepository<Value, Integer>{
public List<Value> findAll();
#RestResource(path = "ids", rel = "findById")
public List<Value> findById(#Param("id") int id) throws ServiceException;
#RestResource(path = "name", rel = "findByName")
public List<Value> findByName(#Param("name") String name) throws ServiceException;
#RestResource(path = "address", rel = "findByAddress")
public List<Value> findByAddress(#Param("address") String address) throws ServiceException;
#RestResource(path = "country", rel = "findByCountry")
public List<Value> findByCountry(#Param("country") String country) throws ServiceException;
}
Hope you can help me putting me in the correct way of what should do or is wrong. If possible some code will also be highly appreciated.
Best regards
You can use #RequestParam("nameParameter")annotation to map all the parameters you want. Let's say you have url like :
http://localhost:8080/data/search/person?name=Will&country=UK
then you can have an api like:
...
#RequestMapping(value = "/person")
public String api(#RequestParam("name") String name, #RequestParam("country") String country)
...

How to sort Global Secondary Index DynamoDB

Data-REST and dynamoDB and trying to sort my GSI object as follows, is it possible to sort GSI Hashkey in dynamoDB,
Domain class
#DynamoDBTable(tableName = "test")
public class Test implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String desc;
#DynamoDBHashKey(attributeName="id")
#DynamoDBAutoGeneratedKey
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#DynamoDBIndexHashKey(attributeName="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#DynamoDBAttribute(attributeName="desc")
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
public interface TestRepository extends PagingAndSortingRepository{
#EnableScan
#EnableScanCount
public Page<Test> findByName(#Param("name") String name, Pageable pageable);
public Page<Test> findByNameOrderByNameAsc(#Param("name") String name, Pageable pageable);
}
Is it possible to use orderBy on secondary Index because am using a hashkey of autogenerated so cant combine with hashkey attribute with Range key to fetch all possible results. I need to fetch names satisfying the search and orderBy name(GSI). Or else DynamoDB supports only orderBy through hashkey and range key as in Demo?
When I do this am getting an exception as "Cant use orderBy on scan query".
UPDATED:
Am using Spring Data DynamoDB, I have created a GSI hash and key index in DynamoDB in the name "description-name-index" and I like to sort its data using range key by findBy of hash key. Here is my test code,
Domain Class
#DynamoDBTable(tableName = "test")
public class Test implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String description;
private String name;
#DynamoDBIndexHashKey(attributeName="description")
public String getDescription() {
return tesId;
}
public void setDescription(String description) {
this.setDescription(description);;
}
#DynamoDBIndexRangeKey(attributeName="name")
public String getName() {
return name;
}
public void setName(String name) {
this.setName(name);
}
#DynamoDBHashKey(attributeName="id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
and tries to fetch and sort data using findByDescriptionOrderByNameDesc(#Param("decription") String description, Pageable pageable) and also I tried using #Id and placed index hash and range key in a class but all in vain. Am getting 500 Internal Error but not throwing any message too. Is there any way to use GSI hash and range pair and sort them.
The error message you get makes sense to me because scan in DynamoDB will return you all the items in random order (i.e. you can't specify order for scan operation).
You want to get all the rows sorted by the name. One naive approach would be to create a GSI with a single dummy hash-key and the range key would be name. All of the rows in that GSI will have the same hashkey. This way GSI can sort all of your rows by name.
The above solution would work but it has a hostspot issue...

Resources