Hibernate criteria subclass attribute - java

I have an Invoice Class like this:
Invoice {
int id;
Set<Attachment> attachments;}
The Attachment class:
Attachment {
int id;
Status status;}
And, The Status class:
Status {
int id;
String desc;}
I want to build a method that given a status element, of an Attachment, return all related invoices.
This is my method:
public List<Invoice> findbyCriteria(Invoice criteria, int init,
int pageSize, String orderBy, String ascDesc) {
Criteria c = getSession().createCriteria(Invoice.class).
add(Example.create(criteria));
if(criteria.getAttachment() !=null && criteria.getAttachment().size() > 0)
c.createCriteria("attachments").add(Example.create((Set<Attachment>)criteria.getAttachments()));
return c.list();
But this returns a ClassCastException during creation the Example:
Example.create((Set<Attachment>)criteria.getAttachments()));
What is wrong?
Thanks!

try this:
List<Invoice> invoices = sess.createCriteria(Invoice.class)
.add(what you need to filter)
.createCriteria("attachments").add(what you need to filter)//like Restrictions.like("name", "F%")
.createCriteria("status").add(what you need to filter).list();

Related

Java how to transform List<Object> to Page from JPA pagination

I have a problem with my custom search method from Jpa Repository. I implement this method to custom search and in this method I have List<Car> from my SQL database and I try to transform it to Page<Car> object from JPA pagination & sorting, but the way I'm trying to do this doesn't work. That is just an illusion of pagination because in postman/internet browser i see all elements from list and when I change page and size value in endpoint that change nothing.
Anybody know how to help?
GitHub project: https://github.com/s0bieskii/DemoCarRental
My endpoint from controller:
#GetMapping("/find")
ResponseEntity<Page<CarDTO>> readAllCarsFiltered(#RequestParam(defaultValue = "0") int page,
#RequestParam(defaultValue = "5") int size,
Pageable pageable,
#RequestParam(required = false) String brand,
#RequestParam(required = false) String model,
#RequestParam(required = false) String type,
#RequestParam(required = false) Integer registrationYear,
#RequestParam(required = false) String color,
#RequestParam(required = false) String fuel,
#RequestParam(required = false) String transmission,
#RequestParam(required = false) String doors,
#RequestParam(required = false, defaultValue = "9999999999") double price) {
return ResponseEntity.ok(carService.customerSearch(pageable,
brand, model, type, registrationYear, color, fuel, transmission, doors, price));
}
My service:
public Page<CarDTO> customerSearch(Pageable pageable, String brand, String model, String type, Integer registrationNumber, String color,
String fuel, String transmission, String doors, double price){
return carRepository.search(pageable, brand, model, type, registrationNumber, color, fuel, transmission, doors, price)
.map(car -> carMapper.carToDto(car));
}
My custom search method from RepositoryImplementation:
#Override
public Page<Car> search(Pageable pageable, String brand, String model, String type, Integer registrationYear, String color,
String fuel, String transmission, String doors, double price) {
int var = 0;
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Car> query = cb.createQuery(Car.class);
Root<Car> car = query.from(Car.class);
query.select(car);
Predicate predicate = cb.greaterThan(car.get("id"), var);
if (brand != null) predicate = cb.and(predicate, cb.equal(car.get("brand"), brand));
if (model != null) predicate = cb.and(predicate, cb.equal(car.get("model"), model));
if (type != null) predicate = cb.and(predicate, cb.equal(car.get("type"), type));
if (registrationYear != null)
predicate = cb.and(predicate, cb.equal(car.get("registrationNumber"), registrationYear));
if (color != null) predicate = cb.and(predicate, cb.equal(car.get("color"), color));
if (fuel != null) predicate = cb.and(predicate, cb.equal(car.get("fuel"), Fuel.stringToFuelEnum(fuel)));
if (transmission != null)
predicate = cb.and(predicate, cb.equal(car.get("transmission"), Transmission.stringToTransmissionEnum(transmission)));
if (doors != null) predicate = cb.and(predicate, cb.equal(car.get("doors"), doors));
if (price != 0) predicate = cb.and(predicate, cb.lessThan(car.get("price"), price));
//predicate = cb.and(predicate, cb.equal(car.get("available"), available));
List<Car> carList=entityManager.createQuery(query.where(predicate)).getResultList();
return new PageImpl<Car>(carList, pageable,carList.size());
}
}
When you do custom queries and want to use Pageable, it's up to you to implement a logic to create a paging for your result list. In your case you aren't doing that so it's probably (you said it isn't working, but didn't say what isn't working so I can only guess) just returning the entire result list without paging applied.
A simple solution would be to use sublist and use the result of that operation as your parameter for PageImpl. Big problem is that you are still getting the entire result list from the database in the background which creates uneceassary overhead.
You could also adjust your query using
em.createQuery(query.where(predicate))
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList()
Now the paging is done in the database which is a lot better.
Or you save yourself some trouble and use Springs Specification interface instead of creating your own search method.
First your CarRepository needs to extend JpaSpecificationExecutor. Now spring will generate a findAll(Specification, Pageable) method for your repository.
Second step is to create your search specification:
public class SearchSpecification implements Specification<Car> {
private final String brand;
private final String model
private final String type
private final Integer registrationYear
private final String color
private final String fuel
private final String transmission
private final String doors
private final double price
// constructor & maybe getter
public Predicate toPredicate(Root<Car> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
// Create your search predicate
}
}
Now you can simply call carRepository.findAll(new SearchSpecification (/* parameters */), pageable); and spring does the paging for you.

How to loop through a list payload and insert into database as individual row items in Springboot?

I am trying to insert items in a list in a database as single values in rows with the name of the sender. I am able to send the payload and insert into a single row with the user detailst. How can I loop through the payload sent and insert all the items into individual rows? I have tried to look for examples no luck. So far I can only insert as a single row in the database
this is the payload
{"labsigned":["234568","234567","2345678","2344556","12335677","2345677","234556","234545"]}
My controller
#RequestMapping(path = "/labreport/createrordispatched", method = RequestMethod.POST)
public ResponseEntity<?> createDispatched(#RequestBody Dispatched dispatched){
if(labDashboardService.createDispatched(dispatched)) {
return ResponseEntity.status(HttpStatus.CREATED).body(true);
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(false);
}
My Service
public boolean createDispatched(Dispatched dispatched) {
dispatched.setCreatedBy(getCurrentUserEmail());
dispatched.setDateCreated(System.currentTimeMillis());
Dispatched ticket = new Dispatched(
dispatched.getCreatedBy(),
dispatched.getDateCreated(),
dispatched.getlabsigned()
);
dispatchedRepository.save(ticket);
return false;
}
My Model
#Entity
#Table(name = "DISPATCHED")
public class Dispatched {
private String id;
private String labsigned;
private Long dateCreated;
private String createdBy;
public Dispatched(){}
public Dispatched(String createdBy, Long dateCreated, String labsigned){
this.labsigned = rorlabsigned;
this.dateCreated = dateCreated;
this.createdBy = createdBy;
}
Assuming that you were able to insert all labsigned in the payload into one single row with the code you mentioned in the question, You should iterate dispatched.labsigned and insert one by one as rows to accomplish what you need. And returning false at the end of method createDispatched will always return HttpStatus.BAD_REQUEST even though the records are successfully saved in the DB, so you might need to change it to return true.
public boolean createDispatched(Dispatched dispatched) {
List<Dispatched> newTickets = new ArrayList<>();
dispatched.setCreatedBy(getCurrentUserEmail());
dispatched.setDateCreated(System.currentTimeMillis());
for(String labSigned:dispatched.getlabsigned()){
Dispatched ticket = new Dispatched(
dispatched.getCreatedBy(),
dispatched.getDateCreated(),
labSigned
);
newTickets.add(ticket);
}
dispatchedRepository.saveAll(newTickets);
return true;
}
Just send in a list of those values. Shouldn't have to be wrapped in a named field on an object. Just send it in as a json array like ["234568","234567","2345678","2344556","12335677","2345677","234556","234545"]. In your controller method, body don't pass it in as Dispatched but instead a List and then just loop through those creating a list of Dispatch objects and then using saveAll in the repository passing in the newly created Dispatched list.
Update: Example without actually compiling. Should be good enough for the example. Also using lombok to make it easier to read and a few other updates.
#AllArgsConstructor
#FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
#RestController
public class DispatchController {
DispatchedEntityFactory dispatchedEntityFactory;
DispatchedRepository dispatchedRepository;
#PostMapping("/labreport/createrordispatched")
public ResponseEntity<Boolean> createDispatched(DispatchedRequest dispatchedRequests){
List<DispatchedEntity> dispatchedEntities = dispatchedEntityFactory.creatMultipleFromDispatchRequest(dispatchedRequests);
if(CollectionUtils.isEmpty(dispatchedEntities)) {
return ResponseEntity.badRequest().body(false);
}
dispatchedRepository.saveAll(dispatchedEntities);
return ResponseEntity.ok(true);
}
}
#Value
public class DispatchedRequest {
String id;
List<String>labsigned;
Long dateCreated;
String createdBy;
}
#Entity
#Table(name = "DISPATCHED")
#Data
#FieldDefaults(level = AccessLevel.PRIVATE)
public class DispatchedEntity {
String id;
String labsigned;
Long dateCreated;
String createdBy;
}
#Component
public class DispatchedEntityFactory {
public List<DispatchedEntity> creatMultipleFromDispatchRequest(final DispatchedRequest dispatchedRequest) {
List<DispatchedEntity> dispatchedEntities = new ArrayList<DispatchedEntity>();
for(String labsignature : dispatchedRequest.getLabsigned()) {
DispatchedEntity dispatchedEntity = new DispatchedEntity(dispatchedRequest.getId(),labsignature, dispatchedRequest.getDateCreated(), dispatchedRequest.getCreatedBy());
dispatchedEntities.add(dispatchedEntity);
}
return dispatchedEntities;
}
}

Dropwizard JDBI 3 ResultSetMapper ignore field

I have this Pojo:
private long id;
#NotEmpty
#JsonProperty("name")
private String name;
#NotEmpty
#JsonProperty("id")
private String tagUuid;
#NotEmpty
#JsonProperty("archived")
private boolean archived;
#NotEmpty
#JsonProperty("creationDate")
private DateTime creationDate;
private Integer count;
#JsonCreator
public Tag() {
}
public Tag(long id, String tagUuid, String name, boolean archived, Timestamp creationDate, Integer count) {
this.id = id;
this.tagUuid = tagUuid;
this.name = name;
this.archived = archived;
this.creationDate = new DateTime(creationDate);
this.count = count;
}
This is my result set mapper:
public class TagMapper implements ResultSetMapper<Tag> {
#Override
public Tag map(int index, ResultSet r, StatementContext ctx) throws SQLException {
return new Tag(
r.getLong("id"),
r.getString("tag_uuid"),
r.getString("name"),
r.getBoolean("archived"),
r.getTimestamp("creation_date"),
r.getInt("count")
);
}
}
How can I fetch from the database one column less. For example in some queries I fetch only tagUuid and name and not the other fields.
But if I do this I get this exception: org.skife.jdbi.v2.exceptions.ResultSetException: Exception thrown while attempting to traverse the result set. I tried to create a addtional Tag Constructor without the other parameters.
This is the query I try to run:
#SqlQuery("SELECT t.id, t.tag_uuid as tag_uuid, t.name, t.archived, t.creation_date FROM tags t WHERE t.tag_uuid = :tag_uuid LIMIT 1")
public Tag fetchTagByUuid(#Bind("tag_uuid") String tagUuid);
You can just return the extra column in your query SQL.
#SqlQuery("SELECT t.id, t.tag_uuid as tag_uuid, t.name, t.archived, " +
"t.creation_date, 0 AS count FROM tags t " +
"WHERE t.tag_uuid = :tag_uuid LIMIT 1")
public Tag fetchTagByUuid(#Bind("tag_uuid") String tagUuid);
You can retrieve the values whatever you want and before passing the values to Tag constructor check their existence in the ResultSet. If the attribute is not present then you can pass the default value for the attributes.
You can check the value as r.getString("tag_uuid") != null (for strings)
then tag_uuid = r.getString("tag_uuid")

"IllegalArgumentException occurred while calling setter for property" from sqlserver numeric to java object

I already have such an error, when you try to perform unapropriate matching (date to boolean, and so) which I had been able to fix quite easilly.
But this time, I am quite confused, because hibernate refuses to match a "numeric" Id to a Java "Long" (and it also failed when setter is made for Double, Integer, Float, String, int, long, etc.)
The sql-server field "id" is a NUMERIC(19,0)
My DTO is :
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
public class DtoResult {
private Long id;
private String name;
// ...
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
My hibernate query :
final SQLQuery query= getCurrentSession().createSQLQuery(select + from + where);
query.setParameter("manyFields", manyFields);
query
.addScalar("id")
.addScalar("name")
.setResultTransformer(Transformers.aliasToBean(DtoResult.class));
return query.list(); // List<DtoResult>
Error:
IllegalArgumentException occurred while calling setter for property [com.some.thing.DtoResult.id (expected type = java.lang.Long)]; target = [com.some.thing.DtoResult#77a70b79], property value = [269895]
I am really puzzled about this, thus any help is welcome.
Thanks for reading untill there.
Just add the expected type, like:
.addScalar("id", new LongType())
.addScalar("name", new StringType())
The number from database query is not Long but BigInteger.
Change setter to:
public void setId(final Number id) {
this.id = id != null ? id.longValue() : null;
}

java service 400 bad request error

Dear spring Java professionals
please help me out in this :
I have a custom service in spring and I dont have any errors on my wildfly server when i run it . but when I do the below update request i am getting 400 bad request though im sending the format as specified in my controller
inside my controller :
#RequestMapping(value = "/user/updatefilters/{Id}", method = RequestMethod.POST)
public Response updateFilter(#PathVariable("Id") Long Id, #RequestBody #Valid Filter Filter) {
FilterService.updateFilter(Id, Filter);
HashMap<String, Object> response = new HashMap<>();
response.put("messages", null);
response.put("success", Boolean.valueOf(true));
return Response.instance().friendlyName("filter-updated").object(response).statusCode(HttpStatus.OK);
}
inside my service file :
public void updateFilter(Long Id,Filter Filter) {
List<Filter> currentFilter = FilterRepo.getFilters(Id, Filter.getFilterId().longValue(),null);
currentFilter.get(0).setLabel(Filter.getLabel());
FilterRepo.save(currentFilter.get(0));
for (FilterField FilterField : Filter.getFilterFields()) {
FilterField currentFilterField = FilterFieldRepo.getFilterField(FilterField.getfieldId());
if (currentFilterField != null) {
currentFilterField.setfield(FilterField.getfield());
currentFilterField.setTypeId(FilterField.getTypeId());
FilterFieldRepo.save(currentFilterField);
}
}
}
inside my repository :
public List<Filter> getFilterList(Long Id, String type) {
List<Filter> FilterField = FilterFieldRepo.getFilterFields(Id,type);
return FilterField;
}
public void updateFilter(Long Id,Filter Filter) {
List<Filter> currentFilter = FilterRepo.getFilters(Id, Filter.getFilterId().longValue(),null);
currentFilter.get(0).setLabel(Filter.getLabel());
FilterRepo.save(currentFilter.get(0));
for (FilterField FilterField : Filter.getFilterFields()) {
FilterField currentFilterField = FilterFieldRepo.getFilterField(FilterField.getfieldId());
if (currentFilterField != null) {
currentFilterField.setfield(FilterField.getfield());
currentFilterField.setTypeId(FilterField.getTypeId());
FilterFieldRepo.save(currentFilterField);
}
}
}
Please note that inside my entity I added a transient list like this :
#Transient
private List<FilterField> filterFields;
updated :
this is my Filter class i generated the crud in netbeans but added the transuent list manually:
#Entity
#Table(schema="hitmeister",name = "filters")
#NamedQueries({
#NamedQuery(name = "Filter.findAll", query = "SELECT s FROM Filter s"),
#NamedQuery(name = "Filter.findByFilterId", query = "SELECT s FROM Filter s WHERE s.filterId = :filterId"),
#NamedQuery(name = "Filter.findById", query = "SELECT s FROM Filter s WHERE s.Id = :Id"),
#NamedQuery(name = "Filter.findByLabel", query = "SELECT s FROM Filter s WHERE s.label = :label"),
#NamedQuery(name = "Filter.findByInsertionDate", query = "SELECT s FROM Filter s WHERE s.insertionDate = :insertionDate"),
#NamedQuery(name = "Filter.findByIsActive", query = "SELECT s FROM Filter s WHERE s.isActive = :isActive"),
#NamedQuery(name = "Filter.findByType", query = "SELECT s FROM Filter s WHERE s.type = :type")})
public class Filter implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "filter_id")
private Integer filterId;
#Basic(optional = false)
#NotNull
#Column(name = "id")
private int Id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 500)
#Column(name = "label")
private String label;
#Basic(optional = true)
#Column(name = "insertion_date")
#Temporal(TemporalType.TIMESTAMP)
private Date insertionDate;
#Column(name = "is_active")
private Boolean isActive;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 20)
#Column(name = "type")
private String type;
#Transient
private List<FilterField> filterFields;
public Filter() {
}
public Filter(Integer filterId) {
this.filterId = filterId;
}
public Filter(Integer filterId, int Id, String label, Date insertionDate, String type) {
this.filterId = filterId;
this.Id = Id;
this.label = label;
this.insertionDate = insertionDate;
this.type = type;
}
public Integer getFilterId() {
return filterId;
}
public void setFilterId(Integer filterId) {
this.filterId = filterId;
}
public int getId() {
return Id;
}
public void setuserId(int Id) {
this.userId = userId;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Date getInsertionDate() {
return insertionDate;
}
public void setInsertionDate(Date insertionDate) {
this.insertionDate = insertionDate;
}
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public int hashCode() {
int hash = 0;
hash += (filterId != null ? filterId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Filter)) {
return false;
}
Filter other = (Filter) object;
if ((this.filterId == null && other.filterId != null) || (this.filterId != null && !this.filterId.equals(other.filterId))) {
return false;
}
return true;
}
#Override
public String toString() {
return " Filter #"+ filterId ;
}
public List<FilterField> getFilterFields() {
return filterFields;
}
public void setFilterFields(List<FilterField> filterFields) {
this.filterFields = filterFields;
}
}
If you need my entity code i can post it as well
Thanks In advance !
My first recommendation: (OP tried and it didn't work, she was sending POST request)
Change your mapping as below and I think you should be fine. Request from browser address bar is a GET request.
As you can see below, HTTP 400 comes when server is unable to understand the request client is sending, and in your case you are sending GET but server has nothing for GET but for POST, so 400.
W3C HTTP 400
10.4.1 400 Bad Request
The request could not be understood by the server due to malformed
syntax. The client SHOULD NOT repeat the request without
modifications.
Code fix:
#RequestMapping(value = "/user/updatefilters/{Id}", method = RequestMethod.GET)
My second recommendation:
I am not Spring expert but here are my 2 cents you can try based on the JSON object you have provided and your Filter mapping - (1.) Change userId to Id, (2.) Have insertionDate as NULL, instead of an empty string.
Make sure your JSON string variables are mapped case-sensitively with your Filter class mapping, and their values are compatible with reference types.
Either your request format is not what Spring expects, or one of the Filter validations is failing. Add a org.springframework.validation.Errors argument and dump the values to find out what validations failed.
public Response updateFilter(#PathVariable("Id") Long Id, #RequestBody #Valid Filter Filter, Errors filterErrors) {
You can sniff the actual traffic using curl or a network monitoring tool to make sure the HTTP transaction is really what you think it is.
EDIT: Having looked at the JSON in one of your comments, I think this is going to turn out to be upper/lower case in your JSON field names. Either change "Id" to "id" and "FilterId" to "filterId", or annotate the Filter fields with #XmlElement(name = "Id") and #XmlElement(name = "FilterId"). Java Bean property names are case sensitive.
EDIT 2: Filter.setuserId(int Id) is broken as well. You need a setId() method for deserializing the bean, and you need to change the method so it stores the passed argument instead of just setting userId to itself.

Categories

Resources