I'm using spring-data -mongodb to do crud operations (create , read , update , delete), but
the delete function doesn't work and I don't know why? . Here is my code.
import org.bson.types.Binary;
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;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
#ToString
#Getter
#Setter
#Document(collection = "patterns")
public class Pattern {
#Id
#Field
private String id;
#Field
#Indexed(unique = true)
private String name;
#Field
private String status;
#Field
private Binary patternFile;
}
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
import fod.pfe7.administratorportal.domain.Pattern;
public interface PatternRepository extends MongoRepository<Pattern, String> {
List<Pattern> findByName(String name);
}
in my controller I do .
patternRepository.findByName(patternName).forEach(pattern -> {
patternRepository.deleteById(pattern.getId());
result.put("status", 0);
result.put("message", "pattern deleted successfuly");
});
Your data might have been created by other system, which use different presentation of "_id" (says String). With newer version of spring data mongodb, you can use #MongoId instead of #Id to control this.
In my case, the later deletes record correctly.
#Id private String id; produces
Remove using query: { "_id" : { "$oid" : "60ed51ce597826297941ade4"}} in collection: sample.
#MongoId(FieldType.STRING) private String id; produces
Remove using query: { "_id" : "60ed51ce597826297941ade4"} in collection: sample.
Maybe a little late...
I got the same error and saw that the delete query did not include the Object_Id, so the findByID method was not working either.
The solution for me was to include the targetType in the #Field annotation:
#Field(name = "_id", targetType = FieldType.OBJECT_ID)
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
/**
* Created by abhinav on 24/02/17.
*/
#MappedSuperclass
#Data
#NoArgsConstructor
public abstract class PersistentObject implements Serializable {
#Version
#Column(name = "version")
private int version = 0;
#Column(name = "updatetime")
private Long updateTime;
#Column(name = "createtime")
private Long createTime= new Date().getTime();
#PrePersist
#PreUpdate
void updateTime() {
this.updateTime = new Date().getTime();
}
}
When i want my entity to include the fields from PersistentObject I have to extend this superclass, Instead of this i was hoping to have a custom annotation mechanism, lets say called by "CustomAnnotation" which i can use on my entities to include the above fields without extending PersistentObject.
Is there a way to do this, Please point in the right direction.
I want to write some query methods in repository layer. This method must ignore null parameters. For example:
List<Foo> findByBarAndGoo(Bar barParam, #optional Goo gooParam);
This method must be return Foo by this condition:
bar == barParam && goo == gooParam;
if gooParam not null. if gooParam was null then condition change to:
bar == barParam;
Is there any solution? Can someone help me?
I don't believe you'll be able to do that with the method name approach to query definition. From the documentation (reference):
Although getting a query derived from the method name is quite
convenient, one might face the situation in which either the method
name parser does not support the keyword one wants to use or the method
name would get unnecessarily ugly. So you can either use JPA named
queries through a naming convention (see Using JPA NamedQueries for
more information) or rather annotate your query method with #Query
I think you have that situation here, so the answer below uses the #Query annotation approach, which is almost as convenient as the method name approach (reference).
#Query("select foo from Foo foo where foo.bar = :bar and "
+ "(:goo is null or foo.goo = :goo)")
public List<Foo> findByBarAndOptionalGoo(
#Param("bar") Bar bar,
#Param("goo") Goo goo);
Too late to answer. Not sure about relationship between Bar and Goo. Check if Example can helps you.
It worked for me. I have a similar situation, entity User have set of attributes and there is findAll method which search user based on attributes(which are optional).
Example,
Class User{
String firstName;
String lastName;
String id;
}
Class UserService{
// All are optional
List<User> findBy(String firstName, String lastName, String id){
User u = new User();
u.setFirstName(firstName);
u.setLastName(lastName);
u.setId(id);
userRepository.findAll(Example.of(user));
// userRepository is a JpaRepository class
}
}
Complementing the answer of #chaserb, I personally would add the parameter as a Java8 Optional type to make it explicit in the signature of the method the semantics that is an optional filter.
#Query("select foo from Foo foo where foo.bar = :bar and "
+ "(:goo is null or foo.goo = :goo)")
public List<Foo> findByBarAndOptionalGoo(
#Param("bar") Bar bar,
#Param("goo") Optional<Goo> goo);
You can use JpaSpecificationExecutor //import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
Step 1: Implement JpaSpecificationExecutor in your JPA Repository
Ex:
public interface TicketRepo extends JpaRepository<Ticket, Long>, JpaSpecificationExecutor<Ticket> {
Step 2 Now to fetch tickets based on optional parameters you can build Specification query using CriteriaBuilder
Ex:
public Specification<Ticket> getTicketQuery(Integer domainId, Calendar startDate, Calendar endDate, Integer gameId, Integer drawId) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get("domainId"), domainId));
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), startDate));
predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), endDate));
if (gameId != null) {
predicates.add(criteriaBuilder.equal(root.get("gameId"), gameId));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
Step 3: Pass the Specification instance to jpaRepo.findAll(specification), it will return you the list of your entity object (Tickets here in the running example)
ticketRepo.findAll(specification); // Pass output of function in step 2 to findAll
So many great answers already, but I specifically implemented this using the answer from #Pankaj Garg (Using the Spring Specification API). There are a few use cases I am adding to my answer
4 parameters that may or may not be null.
Paginated response from the repository.
Filtering by a field in a nested object.
Ordering by a specific field.
First I create a couple of entities, specifically Ticket, Movie and Customer. Nothing fancy here:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.UUID;
#Entity
#Table(name = "ticket", schema = "public")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
public class Ticket implements Serializable {
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id", nullable = false)
private UUID id;
#JoinColumn(name = "movie_id", referencedColumnName = "id", nullable = false)
#ManyToOne(fetch = FetchType.EAGER)
private Movie movie;
#JoinColumn(name = "customer_id", referencedColumnName = "id", nullable = false)
#ManyToOne(fetch = FetchType.EAGER)
private Customer customer;
#Column(name = "booking_date")
#Temporal(TemporalType.TIMESTAMP)
private Date bookingDate;
}
Movie:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
#Entity
#Table(name = "movie", schema = "public")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
public class Movie implements Serializable {
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id", nullable = false)
private UUID id;
#Basic(optional = false)
#NotNull
#Size(max = 100)
#Column(name = "movie_name", nullable = false, length = 100)
private String movieName;
}
Customer:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
#Entity
#Table(name = "customer", schema = "public")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
public class Customer implements Serializable {
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id", nullable = false)
private UUID id;
#Basic(optional = false)
#NotNull
#Size(max = 100)
#Column(name = "full_name", nullable = false, length = 100)
private String fullName;
}
Then I create a class with fields for the parameters I wish to filter by:
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;
import java.util.UUID;
#Data
#AllArgsConstructor
public class TicketFilterParam {
private UUID movieId;
private UUID customerId;
private Date start;
private Date end;
}
Next I create a class to generate a Specification based on the filter parameters. Note the way nested objects are accessed, as well as the way ordering is added to the query.
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TicketSpecifications {
public static Specification<Ticket> getFilteredTickets(TicketFilterParam params) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
if (params.getMovieId() != null) {
predicates.add(criteriaBuilder.equal(root.get("movie").<UUID> get("id"), params.getMarketerId()));
}
if (params.getCustomerId() != null) {
predicates.add(criteriaBuilder.equal(root.get("customer").<UUID> get("id"), params.getDepotId()));
}
if (params.getStart() != null && params.getEnd() != null) {
predicates.add(criteriaBuilder.between(root.get("bookingDate"), params.getStart(), params.getEnd()));
}
criteriaQuery.orderBy(criteriaBuilder.desc(root.get("bookingDate")));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
}
Next I define the Repository interface. This would have not only JpaRepository, but also JpaSpecificationExecutor:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
#Repository
public interface TicketRepository extends JpaRepository<Ticket, UUID>, JpaSpecificationExecutor<Ticket> {
}
Finally, in some service class, I obtain results like this:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
#Service
public class TicketService {
#Autowired
private TicketRepository ticketRepository;
public Page<Ticket> getTickets(TicketFilterParam params, PageRequest pageRequest) {
Specification<Ticket> specification = TicketSpecifications.getFilteredTickets(params);
return ticketRepository.findAll(specification, pageRequest);
}
}
PageRequest and TicketFilterParam would probably be obtained from some parameters and values on a rest endpoint.
You could code this yourself in just a few lines:
List<Foo> findByBarAndOptionalGoo(Bar bar, Goo goo) {
return (goo == null) ? this.findByBar(bar) : this.findByBarAndGoo(bar, goo);
}
Otherwise, I don't know if Spring-Data supports this out of the box.
It is too late too answer, but for anyone who looks for a solution yet there is a more simple way as below, I have faced the same issue and finally could find this solution that looks like very simple and efficient than the others to me:
my Controller Class:
#RestController
#RequestMapping("/order")
public class OrderController {
private final IOrderService service;
public OrderController(IOrderService service) {
this.service = service;
}
#RequestMapping(value = "/{username}/", method = RequestMethod.GET)
public ResponseEntity<ListResponse<UserOrdersResponse>> getUserOrders(
#RequestHeader Map<String, String> requestHeaders,
#RequestParam(required=false) Long id,
#RequestParam(required=false) Long flags,
#RequestParam(required=true) Long offset,
#RequestParam(required=true) Long length) {
// Return successful response
return new ResponseEntity<>(service.getUserOrders(requestDTO), HttpStatus.OK);
}
}
As you can see, I have Username as #PathVariable and length and offset which are my required parameters, but I accept id and flags for filtering search result, so they are my optional parameters and are not necessary for calling the REST service.
my Repository interface:
#Query("select new com.ada.bourse.wealth.services.models.response.UserOrdersResponse(FIELDS ARE DELETED TO BECOME MORE READABLE)" +
" from User u join Orders o on u.id = o.user.id where u.userName = :username" +
" and (:orderId is null or o.id = :orderId) and (:flag is null or o.flags = :flag)")
Page<UserOrdersResponse> findUsersOrders(String username, Long orderId, Long flag, Pageable page);
And that's it, you can see that I checked my optional arguments with (:orderId is null or o.id = :orderId) and (:flag is null or o.flags = :flag) and I think it needs to be emphasized that I checked my argument with is null condition not my columns data, so if client send Id and flags parameters for me I will filter the Result with them otherwise I just query with username which was my #PathVariable.
I'm curious about the following problem. I've two entites, A and B. It stores almost the same information (for example, a name - in real life it's more complex), but the joins, and the foreign keys differs.
Can I do a mapped superclass, without an Id. And class A and class B, extending the mapped superclass, containing only the Id attribute?
For example:
import lombok.Getter;
import lombok.Setter;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
#MappedSuperclass
#Getter
#Setter
#Data
class superClass {
#Column(name = "name")
private String name;
}
#Entity
#Table(name = "A")
#Data
class A extends superClass {
#Id
#OneToMany
#JoinColumn(name = "id", referencedColumnName = "referencedName")
private SomeClass id;
}
#Entity
#Table(name = "B")
#Data
class B extends superClass {
#Id
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "referencedName")
private SomeOtherClass id;
}
Would it be valid by the JPA? I did read the mappedSuperClass's JavaDocs, and says nothing about it. I would said, that it is valid - but the IntelliJ Idea says, that the super class has to have an Id attribute. I didn't find anything on the internet about this.
edit: sorry, I missed it. I left the Entity annotation on the superClass, and that's why the Idea signed the error. I removed that, and the error disappeared. But I'm not sure, that this is valid, though.
yes, there is no requirement that a MappedSuperclass have anything in it. It is just providing extra annotations for the subclasses.
Yes it is valid. Anyway your superclass will not appear as a table in the DB.
I have 4 persistent classes which all have the same fields (exactly) the only 3 difference between them is 1) the class name, 2) the table name and 3) the data. i am aware that this might seem strange to some but trust me there is a good reason which i won't go into here.
now, i'm using hibernate annotations to configure my class which should work like so:
#Entity
#Table(name = "store")
public class Store
{
#Id
#Column(name = "unique_id")
protected String id;
#Column
protected String category;
...
}
.. and this does work, for a single stand-alone class, however there are many fields to map and i'd like to do it all in one hit for all four similar classes, ie:
public class StoreBase
{
#Id
#Column(name = "unique_id")
protected String id;
#Column
protected String category;
...
}
#Entity
#Table(name = "store1")
public class Store1 extends StoreBase
{}
#Entity
#Table(name = "store2")
public class Store2 extends StoreBase
{}
#Entity
#Table(name = "store3")
public class Store3 extends StoreBase
{}
#Entity
#Table(name = "store4")
public class Store4 extends StoreBase
{}
however when attempting this i get the following exception:
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: package.entities.Store1
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:672)
at org.hibernate.cfg.AnnotationConfiguration.processArtifactsOfType(AnnotationConfiguration.java:546)
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:291)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1292)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
i'm guessing this is because the super class is not being searched for the identifier?
is there a way to utilise inheritance in this context?
thanks, paul.
#MappedSuperclass
public class StoreBase
See docs for more info.
Have a look at #MappedSuperclass.