I have a project in spring boot and I'm using CrudRepository, but when I try to update, it doesn't do anything.
#Entity
public class PfmSelection implements Serializable{
#Id
private Integer releaseId;
private String preparedBy;
}
Repositiry
#Repository
public interface IPfmSelectionDao extends CrudRepository<PfmSelection, Integer> {
}
Service
public interface IPfmSelectionService {
public PfmSelection save(PfmSelection pfmSelection);
public PfmSelection findById(Integer id);
}
Service Impl
#Service
public class PfmSelectionService implements IPfmSelectionService {
#Autowired
private IPfmSelectionDao pfmSelectionDao;
#Override
#Transactional
public PfmSelection save(PfmSelection pfmSelection) {
return this.pfmSelectionDao.save(pfmSelection);
}
#Override
#Transactional(readOnly = true)
public PfmSelection findById(Integer id) {
return this.pfmSelectionDao.findById(id).orElse(null);
}
}
Service where I use the other Service
#Autowired
private IPfmSelectionService pfmSelectionService;
private void updatePfm(PushModel pushModel) {
PfmSelection pfm = this.pfmSelectionService.findById(167427);
pfm.setPreparedBy("Rodrige");
pfmSelectionService.save(pfm);
}
I don't receive any error in the console.
You need to take a few steps to know what the problem is
Take the return of pfmSelectionService.save(pfm) and print the saved instance returned like below:
private void updatePfm(PushModel pushModel) {
PfmSelection pfm = this.pfmSelectionService.findById(167427);
pfm.setPreparedBy("Rodrige");
PfmSelection pfm2 = pfmSelectionService.save(pfm);
System.out.println(pfm2.getPreparedBy());
}
Put logger/debugger inside the save method, before and after the save method and check for the entry/exit sop/logger statements in log/console like
#Override
#Transactional
public PfmSelection save(PfmSelection pfmSelection) {
System.out.println("Inside save method");
PfmSelection pfmSelectionSaved =
this.pfmSelectionDao.save(pfmSelection);
System.out.println("Exits save method");
return pfmSelectionSaved;
}
Check for any Aop around advice or any place where the exception is being caught but eaten/not thrown further.
Check if there is any update query fired in the logs at the time of save call.
Also check if the setter method pfm.setPreparedBy("Rodrige"); is Empty?
Related
I am currently working on a project where I have created the following custom Repository:
public interface ServiceRepository<T extends ServiceEntity> extends JpaRepository<T, UUID>, ServiceRepositoryCustom {
}
public interface ServiceRepositoryCustom {
List<ServiceEntity> findAllContainingName(String query);
}
#Repository("Repo")
public class ServiceRepositoryCustomImpl implements ServiceRepositoryCustom {
private final EntityManager em;
public ServiceRepositoryCustomImpl(EntityManager em) {
System.out.println("I got constructed");
this.em = em;
}
#Override
public List<ServiceEntity> findAllContainingName(String name) {
System.out.println("I got called with: " + name);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<ServiceEntity> cq = cb.createQuery(ServiceEntity.class);
Root<ServiceEntity> serviceEntity = cq.from(ServiceEntity.class);
List<Predicate> predicates = new ArrayList<>();
if(name != null) {
// predicates.add(cb.equal(serviceEntity.get("name"), name));
predicates.add(cb.like(serviceEntity.get("name"), name + "%"));
}
cq.where(predicates.toArray(predicates.toArray(new Predicate[0])));
return em.createQuery(cq).getResultList();
}
}
The print statement "I got called with: " never gets called. So for whatever reason Spring Boot is not running the method through my custom implementation.
Any suggestions? Any help is much appreciated
Edit:
Here is the code that injects and uses the Repository in question
#Repository
public interface PineappleServiceRepository extends ServiceRepository<PineappleServiceEntity> {
}
#Component("Registry")
#DependsOn({"Context", "Repo"})
public class Registry {
private final List<ServiceRepository<? extends ServiceEntity>> serviceRepositories = new ArrayList<>();
public Registry(PineappleServiceRepository pineappleServiceRepository) {
this.serviceRepositories.add(pineappleServiceRepository);
}
}
Edit 2:
The code prints "I got constructed"
Edit 3:
Class where findAllContainingName is called
#RestController
#RequestMapping("/test")
#DependsOn("Registry")
public class ServiceController {
private final Registry registry;
public ServiceController(#NotNull Registry registry) {
this.registry = registry;
}
#GetMapping("")
List<ServiceEntity> all(#RequestParam("q") String query) {
return getAllServices(query);
}
private #NotNull List<ServiceEntity> getAllServices(String query) {
List<ServiceEntity> response = new ArrayList<>();
for(ServiceRepository<? extends ServiceEntity> repo: this.registry.getServiceRepositories()){
response.addAll(repo.findAllContainingName(query));
}
return response;
}
}
Edit 4:
Here the entities:
#Entity
#Table(name = "services")
public abstract class ServiceEntity {
protected #Id
UUID id = UUID.randomUUID();
protected String name;
// Constructor + Getters and Setters
}
#Entity
public class PineappleServiceEntity extends ServiceEntity {
// Additional Properties, matching Constructors, Getters and Setters
}
So I was able to reproduce your problem and fix it. Issue with your code is that your PineappleServiceRepository is not extending ServiceRepositoryCustom directly. It seems your repository needs to implement it directly if you are accessing custom repository methods from that repository. I got that idea from this post.
So to fix your issue, either remove PineappleServiceRepository(as you don't have any properties in PineappleEntity) and use ServiceRepository to call that custom method or make PineappleServiceRepository extend ServiceRepositoryCustom.
I have pushed changes to GitHub with fix. You can take a look. If you want to keep PineappleServiceRepository and access custom method using this repository, let me know, I can update code.
I'm newbie with Java. I'm create an API with Webflux and ReactiveMongoRepository.
I try to save my data from another API.
My repository:
#Repository
public interface AccountApiTrackingRepo extends ReactiveMongoRepository<AccountApiTracking, String> {
}
My service implements:
#Override
public void findByIdAccountsApiTrack(User user, String accountId){
AccountApiTracking accountApiTracking = new AccountApiTracking();
accountApiTracking.setUser(user);
accountApiTracking.setAccountId(accountId);
accountApiTracking.setAction("Find account by Id");
System.out.println(accountApiTracking);
accountApiTrackingRepo.save(accountApiTracking);
}
My services:
#Service
public interface AccountApiTrackingService {
public void createAccountsApiTrack(User user, AccountDto accountDto);
public void findByIdAccountsApiTrack(User user, String accountId);
Flux<AccountApiTrackingDto> findAll();
}
My model:
#Document
#Data
#NoArgsConstructor
#AllArgsConstructor
public class AccountApiTracking implements AutoMapping<AccountApiTrackingDto> {
#Id
private String id;
private User user;
private String action;
private AccountDto payload;
private String accountId;
#Override
public String toString() {
return String.valueOf(action);
}
}
After function findByIdAccountsApiTrack() run I can't find any document created in my database.
I can see my variable accountApiTracking have data. But accountApiTrackingRepo.save doesn't work.
What I'm missing?
In Reactive programming should subscribe to any reactive instruction.
you should
return accountApiTrackingRepo.save(accountApiTracking);
to till your usage for example in the controller.
or for your test case, you can use ".subscribe()" after your method call.
I'm creating an application based on Hazelcast and spring-boot-starter-web. The application structure is:
Book controller-> BookService interface-> BookServiceImplementation-> put into hazelcast queue
Author controller-> AuthorService interface-> AuthorServiceImplementation-> put into hazelcast queue.
For that purpose i need one class that contains hazelcastInstance to share it in all services so i created blackboard interface but because i use #Autowired it is creating new instance for every service and i need to set hazelcast instance again.
My code so far:
Controller:
#GetMapping("book")
public ResponseEntity<Void> getBookDetails(
#RequestParam(value = "bookId", required = false) Long bookId) {
bookService.add(bookId);
return new ResponseEntity<Void>(HttpStatus.OK);
}
Service impl:
#Service("bookService")
public class BookServiceImpl extends BaseService implements BookService {
#Override
public void add(Long bookId) {
blackboard.add(bookId, Listeners.BOOK_QUEUE_NAME);
}
BaseService:
public class BaseService {
#Autowired
protected Blackboard blackboard;
public void loadInstance(ClientConfig clientConfig) {
HazelcastInstance hazelcastInstanceClient = HazelcastClient.newHazelcastClient(clientConfig);
blackboard.setHazelcastInstance(hazelcastInstanceClient);
}
}
Blackboard interface impl:
#Component("blackboard")
public class BlackboardImpl implements Blackboard {
private HazelcastInstance hazelcastInstance;
#Override
public HazelcastInstance getHazelcastInstance() {
return hazelcastInstance;
}
#Override
public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
this.hazelcastInstance = hazelcastInstance;
}
#Override
public boolean add(Object obj, String collectionId) {
IQueue<Object> queue = hazelcastInstance.getQueue(collectionId);
return queue.add(obj);
}
}
I was reading this link about Spring Data JPA and it got me curious: Instead of using #Query annotation, can you create a query and then use it as a param to the method?
More like this:
#Repository
public interface MyRepository extends CrudRepository<MyClass, Integer>
{
void doSomething(Query query);
}
(BTW, I know I could implement a fragment repository and solve my problem, but I'm curious)
you could not create an implementation class, instead of that you can write interface methods like this:
#Entity
public class Part {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#Column(unique = true)
private String partId;
public Part() {
}
public Part(String partId) {
this.partId = partId;
}
public String getPartId() {
return partId;
}
public void setPartId(String partId) {
this.partId = partId;
}
public Set<Card> getCards() {
return cards;
}
}
public interface PartRepository extends CrudRepository<Part, Long> {
public Optional<Part> findByPartId(String partId);
public List<Part> findAllByPartId(String partId);
}
Spring automatically convert these lines to SQL in background, you should don't care about that.
You can find some details here: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods
This question already has answers here:
Update or saveorUpdate in CRUDRepository
(2 answers)
Closed 3 years ago.
I am processing a Post request with Json data, how do I add them to the database, so that there is a check that finds out if there is already this user in the database or not, if not, then create a new row, if there is, then update messages for this user.
What I accept in the request: phone, message
I do not quite understand how to implement it.
UserController
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserRepo userRepo;
#PostMapping(consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public String createUser(#Valid #RequestBody User requestUserDetails) {
userRepo.save(requestUserDetails);
return "The message delivered.";
}
}
User
#Entity
#Table(name = "ApiTable", schema = "TestApi")
public class User {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
private String phone;
private String message;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getMessage() {
return message;
}
public void setLastName(String message) {
this.message = message;
}
}
UserRepo
public interface UserRepo extends CrudRepository<User, Long> {
}
Application
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
KeyUser
public class KeyUser implements Serializable {
private String phone;
private String message;
}
Application
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Well, the userRepo.save(requestUserDetails) already do it for you.
Look at the implementation of save method in SimpleJpaRepository<T, ID> class from Spring.
SimpleJpaRepository.java
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
org.springframework.data.jpa.repository.support.SimpleJpaRepository<T, ID> implements the JpaRepository<T, ID>, which is inherited by CrudRepository<T, ID>.
This already check if the object already exists on the database. All you have to supply is your entity with the primary key populated.
If the primary key is not populated, then Spring can't check if the object exists. So a new row will be created.
If the object already exists, it will call EntityManager.merge if not, it will call EntityManager.persist.
Take a look:
User use = new User();
user.setId(1);
user.setPhone("123");
user.setMessage("Hello");
userRepository.save(user);
In this case, if the User with id == 1 exists on the the database, it's information will be merged.
Now, if, for some reason, this don't fit for you, then you will have to do it manually, by searching for the user by it's ID and then applying some rule to it.
Something like this:
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserRepo userRepo;
#PostMapping(consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public String createUser(#Valid #RequestBody User requestUserDetails) {
User user = requestUserDetails;
if(requestUserDetails.getId() != null) {
Optional<User> userOpt = userRepo.findById(requestUserDetails.getId());
if (userOpt.isPresent()) {
user = userOpt.get();
// the user exists... do something..
}
}
// at the end, save the user anyway (event if it exists or not)
userRepo.save(user);
return "The message delivered.";
}
}
to update the object you can use the merge method. If the object has the id attribute, use merge, else, save.