ReactiveMongoRepository can't save to database - java

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.

Related

Override write concern on an individual operation or query in project that uses spring data

I am working spring boot project that uses spring data as an abstraction to access the database[MongoDB]. I want to change the write concern only for two specific operations.
Below is the entity and repository class that I use to access the Mongo DB collection:
Entity
#Document(collection = "tests")
#Data
#NoArgsConstructor
public class Test {
#Id
private String id;
private String name;
private String category;
}
Repository
#Repository
public interface TestRepository extends BaseMongoRepository<Test> {
...
#DeleteQuery(value="{'id':?0}, { writeConcern: { w : '2', wtimeout : 1000 }, delete=true")
void safeDeleteByTestId(String id,String name);
default void updateNameForAll(String category) {
final Query query = query(where("category").is(category);
final Update update = Update.update("name", name);
getMongoOperations().updateMulti(query, updategetMetadata().getCollectionName());
}
...
}
How can I modify the updateNameForAll method to increase the write concern only for this query? I don't want to override write concern for the entire collection or database.
Kind Regards,
Rando.
I found a workaround to this issue:
I created a new interface named ETestRepository like below:
public interface ETestRepository {
void safeUpdateNameForAll(String category);
}
Then I created a implementation of the interface:
public interface ETestRepositoryImpl implements ETestRepository {
#Autowired
private MongoTemplate mongoTemplate;
#Override
public void safeUpdateNameForAll(String accountId, String contextId, ChangeSetRowAction action) {
mongoTemplate.setWriteConcern(WriteConcern.W2);
final Query query = query(where("category").is(category);
final Update update = Update.update("name", name);
mongoTemplate.updateMulti(query,update, Test.class);
}
}
In the end, I modified the TestRepository interface to extend the ETestRepository interface to include the safeUpdateNameForAll method.

Spring CrudRepository Update doesn't work

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?

Can you send a Query object as parameter to a SpringData Repository method?

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

Efficient way of reusing multiple Services having the same functionality but dealing with different entity

I am dealing with multiple entity classes but they have the same attributes.
It's not a good data structure but according to my use case, I have to deal with two entities having the same attributes. As of now I am using multiple controllers, multiple request/response and multiple interfaces and implementations to do the logic part and save into the two corresponding tables.
Is there any efficient way that I could combine both into a single interface, with the DB operations happening in multiple DBs.
Following is a sample code:
#RestController
..
public class FooController {
#Autowired
private FooService fooService;
#GetMapping
public FooResponse findFoo(
#PathVariable(FOO_CONSTANT.PATH_VARIABLE_FOO_ID) String fooId)
throws FooException {
return fooService.findFoo(fooId));
}
}
public interface FooService{
FooResponse findFoo(String fooId);
}
#Service
public class ProspectAssetServiceImpl implements ProspectAssetService {
#Autowired
private FooRepository fooRepository;
#Override
public FooResponse findFoo(String fooId){
FooEntity fooEntity = fooRepository.findByFooId(fooId));
return convertBomToMessaging(fooEntity);
}
}
public class Foo {
private String fooId;
private String fooName;
//getters and setters
}
#Entity
#Table(name = "foo_table")
public class FooEntity {
private String fooId;
private String fooName;
//getters and setters
}
Second Service:
#RestController
..
public class ZooController {
#Autowired
private ZooService ZooService;
#GetMapping
public ZooResponse findZoo(
#PathVariable(Zoo_CONSTANT.PATH_VARIABLE_Zoo_ID) String ZooId)
throws ZooException {
return ZooService.findZoo(ZooId));
}
}
public interface ZooService{
ZooResponse findZoo(String ZooId);
}
#Service
public class ProspectAssetServiceImpl implements ProspectAssetService {
#Autowired
private ZooRepository ZooRepository;
#Override
public ZooResponse findZoo(String ZooId){
ZooEntity ZooEntity = ZooRepository.findByZooId(ZooId));
return convertBomToMessaging(ZooEntity);
}
}
public class Zoo {
private String ZooId;
private String ZooName;
//getters and setters
}
#Entity
#Table(name = "Zoo_table")
public class ZooEntity {
private String ZooId;
private String ZooName;
//getters and setters
}
Suggest me an efficient way to combine these two together?

How to properly publish DDD domain events with spring?

I am trying to implement domain driven design in my project.
Here is my base Aggregate class:
public abstract class UUIDAggregate {
private final DomainEventPublisher domainEventPublisher;
protected void publish(DomainEvent domainEvent) {
domainEventPublisher.publish(domainEvent);
}
}
Let's say we have UserAccount aggregate:
public class UserAccount extends UUIDAggregate {
private String email;
private String username;
private String password;
private String firstName;
private String lastName;
public void update() {
publish(new DomainEventImpl());
}
}
Here is my DomainEventPublisher:
public interface DomainEventPublisher {
void publish(DomainEvent event);
}
Here is DomainEventPublisherImpl:
#Component
public class DomainEventPublisherImpl implements DomainEventPublisher{
#Autowired
private ApplicationEventPublisher publisher;
public void publish(DomainEvent event){
publisher.publishEvent(event);
}
}
Now, this seems like a good idea, the domain is separated from implementation but this does not work. DomainEventPublisher cannot be Autowired because UUIDAggregate is not a #Component or #Bean . One solution would be to create DomainService and publish event there but that seems like leaking of domain to domain service and if I go that way, I am going to anemic model. Also what I can do is to pass DomainEventPublisher as a parameter to every aggregate but that also does not seems like a good idea.
One idea would be to have a factory for domain objects:
#Component
class UserAccountFactoryImpl implements UserAccountFactory {
#Autowired
private DomainEventPublisher publisher;
#Override
public UserAccount newUserAccount(String email, String username, ...) {
return new UserAccount(email, username, ..., publisher);
}
}
Then your code creating a domain object is "publisher-free":
UserAccount userAccount = factory.newUserAccount("john#example.com", ...);
Or you might slightly change the design of the event-publishing:
public abstract class UUIDAggregate {
private final List<DomainEvent> domainEvents = new ArrayList<>();
protected void publish(DomainEvent domainEvent) {
domainEvents.add(domainEvent);
}
public List<DomainEvent> domainEvents() {
return Collections.unmodifiableList(domainEvents);
}
}
#Component
class UserAccountServiceImpl implements UserAccountService {
#Autowired
private DomainEventPublisher publisher;
#Override
public void updateUserAccount(UserAccount userAccount) {
userAccount.update();
userAccount.domainEvents().forEach(publisher::publishEvent);
}
}
This is different from your proposal: the service publishes the events, but doesn't create then - the logic stays in the domain object.
Further, you can change your publisher to minimize the boiler-plate code:
public interface DomainEventPublisher {
void publish(UUIDAggregate aggregate);
}
Vaughn Vernon in his book IDDD just uses singleton like this:
DomainEventPublisher.instance().register(...);
DomainEventPublisher.instance().publish(...);
I know this approach doesn't use spring injection but it's much simplier than passing publisher to every aggregate and not that hard to test.

Categories