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
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.
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?
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
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?
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.