How do you set WriteConcern using spring mongodb to ACKNOWLEDGED? - spring-mongodb

I want to set the WriteConcern in spring mongodb to acknowledged. Also, I'm wondering if this is the default value? I am using the spring.data.mongodb.uri in my application.properties so I don't have any mongo configuration class.

From the docs of SpringData here
9.4.3. WriteConcern
You can set the com.mongodb.WriteConcern property that the MongoTemplate will use for write operations if it has not yet been specified via the driver at a higher level such as com.mongodb.Mongo. If MongoTemplate’s WriteConcern property is not set it will default to the one set in the MongoDB driver’s DB or Collection setting.
9.4.4. WriteConcernResolver
For more advanced cases where you want to set different WriteConcern values on a per-operation basis (for remove, update, insert and save operations), a strategy interface called WriteConcernResolver can be configured on MongoTemplate. Since MongoTemplate is used to persist POJOs, the WriteConcernResolver lets you create a policy that can map a specific POJO class to a WriteConcern value. The WriteConcernResolver interface is shown below.
public interface WriteConcernResolver {
WriteConcern resolve(MongoAction action);
}
Find a direct implementation here

you can do this over Spring bean
#Configuration
public class MongoConfiguration {
#Bean
public WriteConcernResolver writeConcernResolver() {
return action -> {
System.out.println("Using Write Concern of Acknowledged");
return WriteConcern.ACKNOWLEDGED;
};
}
}

It is not sufficient to only provide a WriteConcernResolver over Bean configuration.
The MongoTemplate will not use it.
To make this happen you have to create a class like this with two options to set the WriteConcern:
import com.mongodb.WriteConcern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.WriteResultChecking;
import org.springframework.data.mongodb.core.convert.MongoConverter;
#Configuration
public class MongoConfiguration {
Logger logger = LoggerFactory.getLogger(MongoConfiguration.class);
public MongoConfiguration() {
logger.info("MongoConfiguration applied ...");
}
#Bean
MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDbFactory, MongoConverter converter) {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);+
// Version 1: set statically
logger.debug("Setting WriteConcern statically to ACKNOWLEDGED");
mongoTemplate.setWriteConcern(WriteConcern.ACKNOWLEDGED);
// Version 2: provide a WriteConcernResolver, which is called for _every_ MongoAction
// which might degrade performance slightly (not tested)
// and is very flexible to determine the value
mongoTemplate.setWriteConcernResolver(action -> {
logger.debug("Action {} called on collection {} for document {} with WriteConcern.MAJORITY. Default WriteConcern was {}", action.getMongoActionOperation(), action.getCollectionName(), action.getDocument(), action.getDefaultWriteConcern());
return WriteConcern.ACKNOWLEDGED;
});
mongoTemplate.setWriteResultChecking(WriteResultChecking.EXCEPTION);
return mongoTemplate;
}
}

You can set the write-concern in the xml-configuration (if applicable)
<mongo:db-factory ... write-concern="SAFE"/>

Related

Can I use mongock with Spring Data Repositories?

I tried to inject with #Autowired annotation a repository into changelog
and it doesn't get injected.
Config uses spring application context
#Bean
public SpringBootMongock mongock(ApplicationContext springContext, MongoClient mongoClient) {
return new SpringBootMongockBuilder(mongoClient, "yourDbName", "com.package.to.be.scanned.for.changesets")
.setApplicationContext(springContext)
.setLockQuickConfig()
.build();
}
And the changelog
#ChangeLog(order = "001")
public class MyMigration {
#Autowired
private MyRepository repo;
#ChangeSet(order = "001", id = "someChangeId", author = "testAuthor")
public void importantWorkToDo(DB db){
repo.findAll() // here null pointer
}
}
firstly, notice that if you are using repositories in your changelogs, it's a bad practice to use it for writes, as it won't be covered by the lock mechanism(this is feature is coming soon), only for reads.
To inject your repository(or any other dependency) you simply need to inject it in your changeSet method signature, like this:
#ChangeLog(order = "001")
public class MyMigration {
#ChangeSet(order = "001", id = "someChangeId", author = "testAuthor")
public void importantWorkToDo(MongoTemplate template, MyRepository repo){
repo.findAll(); this should work
}
}
Notice that you should use the last version(at this moment 3.2.4) and DB class is not supported anymore. Please use MongoDatabase or MongoTemplate(preferred).
Documentation to Mongock
we have recently released the version 4.0.7.alpha, which among other things allows you to use Spring repositories(and any other custom bean you wish) in your changeSets with no problem. You can insert, update, delete and read. It will be safely covered by the lock.
The only restriction is that it needs to be an interface, which should be the common case for Spring repositories.
Please take a look to this example

Spring data JPA waiting find

I have some method which import prices for airports and save it to database through Spring data JPA repositories.
Import method is this:
#Transactional
public Future<Boolean> importFuel(File serverFile, Long providerIdLong) {
final FuelProvider fuelProvider = fuelProviderRepository
.findOne(providerIdLong);
LOG.debug("fuelProvider:" + fuelProvider.getName());
List<AirportFuel> airportFuels = processors
.get(providerIdLong).process(serverFile, fuelProvider);
if(airportFuels==null){
return new AsyncResult<>(false);
}
airportFuelRepository.deleteByFpId(providerIdLong);
airportFuelRepository.save(airportFuels);
fuelProvider.setUpdated(new Date());
fuelProviderRepository.save(fuelProvider);
return new AsyncResult<>(true);
}
For example this is read method:
List<AirportFuel> airportFuels = airportFuelRepository.findByIata(airport.getIata());
and AirportFuelRepository:
#Repository
#Transactional(readOnly = true)
public interface AirportFuelRepository extends CrudRepository<AirportFuel,
Long> {
#Transactional
#Modifying
#Query("delete from AirportFuel af where af.fpId = :#{#fpId}")
void deleteByFpId(#Param("fpId") Long fpId);
#Transactional(readOnly = true)
List<AirportFuel> findByIata(String iata);
List<AirportFuel> findByIataAndFpId(String iata, Long fpId);
}
Application is based on Spring boot, Hibernate, Spring data JPA and MS SQL.
If import method running then other method which also use airportFuelRepository waiting for end of this method and i do not know why. I suppose the reading method will work with the data before the import and will not waiting to end import method.
Thank you for advice.
If I understand correctly you want to have both methods be able to access the same repository asynchronously.
In order to do this annotate your method with the #Async annotation.
A few links to get you started:
baeldung.com
Spring Quickstart
Spring Javadoc
Dzone

SQLServerException: "The result set is closed" when using Streams as return type

When trying to stream results from Microsoft SQL Server 2014 I always get an SQLServerException: The result set is closed. Also tried with selectMethod=cursor in the JDBC URL to no avail. Is there something MSSQL specific to consider? Using Spring Boot 1.4.0.RELEASE, Spring Data JPA 1.10.2.RELEASE.
Sample repository interface:
package sample;
import sample.Contact;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.stream.Stream;
#Repository
public interface MyRepository extends JpaRepository<Contact, String> {
Stream<Contact> findByContactid(String contactid);
}
I've found that making the service method that is calling the repository be inside of a transaction gets rid of this error for me.
#Transactional(readOnly=true)
public List<Results> myServiceMethod() {
return repo.findByContactid("12345").map(c->c.getName()).collect(toList());
}

java ee 7 : using interceptors for centralized logging

I'd like to have one class responsible for my logging. The way I understand it, that's what interceptors can be used for. Here's my interceptor:
package logging;
import java.io.Serializable;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
public class LoggingInterceptor implements Serializable {
/**
*
*/
private static final long serialVersionUID = -2095670799863528243L;
#AroundInvoke
public Object intercept(InvocationContext context) throws Exception {
System.out.println("before calling method :"
+ context.getMethod().getName());
Object[] params = context.getParameters();
for (Object param : params) {
System.out.println("PARAM " + param.toString());
}
Object result = context.proceed();
System.out.println("after calling method :"
+ context.getMethod().getName());
return result;
}
}
I annotated various methods with the #Interceptor annotation.
My question is: How do I differentiate between the methods that are called? Depending on the method, I want to log a different message and maybe some parameters like folder names.
Right now, the only thing I can think of is a big if-elseif-else or switch statement to check the name of the method.
But this seems to be poor design. Am I using the interceptor for the right purpose? And if so, how would I go about implementing logging in a clean way?
I'd like to have one class responsible for logging because I want to display methods on the user interface as well as log to a file. Also, I'd like to use some java ee 7 built-in java ee 7 mechanism for such a cross-cutting concern.

#EnableAspectJAutoProxy not work with proxyTargetClass=false

I am learning about Spring AOP at first time.
I am reading about in this sites:
Site2 and
Site1
Following this I have made the next classes
Main class:
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.refresh();
MessagePrinter printer = context.getBean(MessagePrinter.class);
System.out.println(printer.getMessage());
}
}
App config class:
#Configuration
#ComponentScan("com.pjcom.springaop")
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig {
#PostConstruct
public void doAlert() {
System.out.println("Application done.");
}
}
Aspect class:
#Component
#Aspect
public class AspectMonitor {
#Before("execution(* com.pjcom.springaop.message.impl.MessagePrinter.getMessage(..))")
public void beforeMessagePointCut(JoinPoint joinPoint) {
System.out.println("Monitorizando Mensaje.");
}
}
And others...
Just like that app work nice, but if I put proxyTargetClass to false. Then I get the error below.
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.pjcom.springaop.message.impl.MessagePrinter] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:318)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:985)
at com.pjcom.springaop.App.main(App.java:18)
Why?
#EnableAspectJAutoProxy(proxyTargetClass=false)
Indicates that JDK dynamic proxy will be created to support aspect execution on the object. And therefore as this type of proxy requires a class to implement an interface your MessagePrinter must implement some interface which declares method getMessage.
#EnableAspectJAutoProxy(proxyTargetClass=true)
On the opposite instruct to use CGLIB proxy which is able to create proxy for a class without an interface.
1> Message Printer has to be defined as a component i.e :
`
package com.pjcom.springaop.message.impl;
#Component
public class MessagePrinter{
public void getMessage(){
System.out.println("getMessage() called");
}
}`
in the same package as configuration java file if no #ComponentScan is not defined for some other packages.
2> If same type of bean class has many other dependencies then to resolve dependencies in spring Config use #Qualifier annotation.

Resources