How to log actual target class name for Spring Data Repository when using CustomizableTraceInterceptor - spring-data

My motivation is to easily find out during maintenance of a large Spring Data Jpa project which Repository method generated given sql.
I have CustomerRepository as in GitHub spring-data-examples.
I changed CustomizableTraceInterceptor to:
public #Bean CustomizableTraceInterceptor interceptor() {
CustomizableTraceInterceptor interceptor = new CustomizableTraceInterceptor();
interceptor.setHideProxyClassNames(true);
interceptor.setEnterMessage("Entering $[targetClassShortName].$[methodName]()");
return interceptor;
}
I would like to see in the log:
Entering CustomerRepository.save()
but instead I am getting:
Entering SimpleJpaRepository.save()
Thanks a lot for your help.

I solved the problem by extending CustomizableTraceInterceptor:
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.interceptor.CustomizableTraceInterceptor;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
public class MethodTraceInterceptor extends CustomizableTraceInterceptor {
#Override
protected Class<?> getClassForLogging(Object target) {
Class<?> classForLogging = super.getClassForLogging(target);
if (SimpleJpaRepository.class.equals(classForLogging)) {
Class<?>[] interfaces = AopProxyUtils.proxiedUserInterfaces(target);
if (interfaces.length > 0) {
return interfaces[0];
}
}
return classForLogging;
}
}
but I find it strange that such overriding is necessary. Ideally Spring trace interceptors should resolve class for logging correctly, even for spring data repositories.

Related

Micronauts Failed to inject value for parameter [entityManager] of class: UserRepository

I'm trying to inject EntityManager in the repository.
compilation was successful but when i run the application and send a post request i got this error:
Unexpected error occurred: Failed to inject value for parameter [entityManager] of class: aututor.apps.DAO.Repository.RepositoryImp.UserRepository
Message: No bean of type [javax.persistence.EntityManager] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
Path Taken: new UserController([UserService service]) --> new UserServiceImpl([IUserRepository userRepository]) --> new UserRepository([EntityManager entityManager])
io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [entityManager] of class: aututor.apps.DAO.Repository.RepositoryImp.UserRepository
this is the repository :
package aututor.apps.DAO.Repository.IRepository;
import aututor.apps.DAO.Model.*;
public interface IUserRepository {
public User save(User User);
public User findByEmail(String Email);
}
package aututor.apps.DAO.Repository.RepositoryImp;
import aututor.apps.DAO.Model.User;
import aututor.apps.DAO.Repository.IRepository.IUserRepository;
import io.micronaut.configuration.hibernate.jpa.scope.CurrentSession;
import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#Singleton
public class UserRepository implements IUserRepository {
#PersistenceContext
protected EntityManager entityManager;
public UserRepository(#CurrentSession EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public User save(User User) {
return entityManager.merge(User);
}
#Override
public User findByEmail(String Email) {
return entityManager.createQuery("SELECT user FROM User user WHERE user.Email = :email", User.class)
.setParameter("email", Email)
.getSingleResult();
}
}
this is the service :
package aututor.apps.Services.ServicesImpl;
import aututor.apps.DAO.Model.User;
import aututor.apps.DAO.Repository.IRepository.IUserRepository;
import aututor.apps.DTO.Mapper.UserMapper;
import aututor.apps.DTO.UserDTO;
import aututor.apps.Services.IServices.UserService;
import aututor.apps.Util.Response;
import javax.inject.Singleton;
#Singleton
public class UserServiceImpl implements UserService {
protected final IUserRepository userRepository;
protected final UserMapper userMapper= new UserMapper();
public UserServiceImpl(IUserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public Response CreateUser(UserDTO User) {
if(userRepository.findByEmail(User.getEmail())==null) {
User u = userRepository.save(userMapper.ToUser(User));
return new Response("request has succeeded",userMapper.ToUserDTO(u),200);
}
else {
return new Response("Not Acceptable : Adress email already exist",null,406);
}
}
#Override
public Response UpdateUser(UserDTO User) {
return null;
}
#Override
public Response DeleteUser(UserDTO User) {
return null;
}
#Override
public Response FindUserByID(Long Id) {
return null;
}
}
Controller :
package aututor.apps.Controllers;
import aututor.apps.DTO.UserDTO;
import aututor.apps.Services.IServices.UserService;
import aututor.apps.Util.Response;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
#Controller("/rest/User")
public class UserController {
protected final UserService service ;
public UserController(UserService service) {
this.service = service;
}
#Post("/")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response CreateUser(#Body UserDTO user) {
return service.CreateUser(user);
}
}
build.gradle :
dependencies {
annotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion")
annotationProcessor "io.micronaut:micronaut-inject-java"
annotationProcessor "io.micronaut:micronaut-validation"
annotationProcessor "io.micronaut.data:micronaut-data-processor:1.0.0"
implementation platform("io.micronaut:micronaut-bom:$micronautVersion")
implementation "io.micronaut:micronaut-security-jwt"
implementation "io.micronaut:micronaut-http-client"
implementation "io.micronaut:micronaut-validation"
implementation "io.micronaut:micronaut-http-server-netty"
implementation "io.micronaut.configuration:micronaut-hibernate-jpa"
implementation("io.micronaut:micronaut-spring")
compile "io.micronaut.configuration:micronaut-jdbc-tomcat"
compile "io.micronaut:micronaut-runtime"
compile "io.micronaut:micronaut-inject"
compile("io.micronaut.data:micronaut-data-hibernate-jpa")
compile("io.micronaut.data:micronaut-data-jdbc")
compile("org.postgresql:postgresql:42.2.1")
runtimeOnly "ch.qos.logback:logback-classic:1.2.3"
testAnnotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion")
testAnnotationProcessor "io.micronaut:micronaut-inject-java"
testImplementation platform("io.micronaut:micronaut-bom:$micronautVersion")
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "io.micronaut.test:micronaut-test-junit5"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine"
}
application.yml:
---
micronaut:
application:
name: apps
server:
port: 8089
---
datasources:
default:
url: jdbc:postgresql://localhost:5432/db
username: root
password: ''
schema-generate: CREATE_DROP
jpa:
default:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.PostgreSQL95Dialect
entity-scan:
packages: 'apps.model'
i have tried to work with CrudRepository and there was a problem there also. Everything looks correct i don't know if i miss something.
I started working with Micronaut JPA and Hibernate and came across the same kind of issue that you have. Whatever scans the application.yml is not good at all in telling you if you didn't define your jpa settings properly. After getting experience from beating my head over for days trying to get Micronaut to work with data (and on the brink of giving up on using Micronaut from accessing data at all) from looking at your setup, my best bet is you defined your entity-scan packages wrong. If you specify an entity scan package it must be a package that encapsulates or is a parent encapsulating package of your classes that are annotated with #Entity. In your case you specified apps.model as your package, but I see no where in the code you provided where you are referencing anything that follows that package format. Not sure why you have a aututor.apps.DAO.Model.User package. Model should be separate from your DAO. The easiest thing would be to just delete entity-scan packages and let all your classes get scanned. Specifying the package is supposed to improve performance, but if you don't have a lot of files I don't think it really makes a difference. You could have specified aututor or aututor.apps or aututor.apps.DAO or aututor.apps.DAO.Model as your scan package and that should work if your classes with #Entity are in aututor.apps.DAO.Model package.
apply annotation #Repository on your repository class.

How to configure a Listener to override the behaviour of the class LoggingReporter in Citrus?

I'm looking for a way to remove stacktraces of fails assertions when using the framework Citrus.
This is done in testNg like this:
public class NoStackTraceListener implements ITestListener {
...
#Override
public void onTestFailure(ITestResult iTestResult) {
Throwable th = iTestResult.getThrowable();
if (Objects.nonNull(th)) {
System.out.println(th.getMessage());
iTestResult.setThrowable(null);
}
}
...
}
#Listeners({ NoStackTraceListener.class })
class A {...}
But I can't find any example of usgin the class 'TestListener' or others in order to override the supplied implementation of 'LoggingReporter'
Please do anyone has already overrided a Listener using framework citrus and could give the snippet to do so ?
Thanks
You need to add the custom reporter as bean to the Spring application context:
#Bean
public NoStackTraceReporter noStackTraceReporter() {
return new NoStackTraceReporter();
}
You can also overwrite the default logging reporter by choosing the bean name loggingReporter
#Bean
public NoStackTraceReporter loggingReporter() {
return new NoStackTraceReporter();
}
The NoStackTraceReporter implementation is then able to overwrite the specific event handler for failed tests:
public class NoStackTraceReporter extends LoggingReporter {
...
#Override
public void onTestFailure(TestCase test, Throwable cause) {
// do something customized
}
...
}
Also you may overwrite the generateTestResults() method in the reporter interface in order to customize logging results.
You can also follow the sample http://www.citrusframework.org/samples/reporting/ that demonstrates how to add customized reporters in Citrus.

Implementing Projection with Specification in Spring Data JPA

I am trying to implement the projection with specification in Spring Data JPA via this implementation:
https://github.com/pramoth/specification-with-projection
Related classes are as follows:
Spec:
public class TopicSpec {
public static Specification<Topic> idEq(String id){
return (root, query, cb) -> cb.equal(root.get(Topic_.id),id);
}
}
Repository
#Repository
public interface TopicRepository extends JpaRepository<Topic,String>,JpaSpecificationExecutorWithProjection<Topic> {
public static interface TopicSimple{
String getId();
String getName();
}
List<TopicSimple> findById(String id);
}
Test
#Test
public void specificationWithProjection() {
Specification<Topic> where= Specifications.where(TopicSpec.idEq("Bir"));
List<Topic> all = topicRepository.findAll(where);
Assertions.assertThat(all).isNotEmpty();
}
I have this response from the Get method:
However the tests fail. Besides when I pull the github project of pramoth I can run the tests with success. Does anyone have any opinion about this issue?
The full project can be found here:
https://github.com/dengizik/projectionDemo
I have asked the same question to the developer of the project Pramoth Suwanpech, who was kind enough to check my code and give answer. My test class should've implement the test object like this:
#Before
public void init() {
Topic topic = new Topic();
topic.setId("İki");
topic.setName("Hello");
topicRepository.save(topic); }
With this setting the tests passed.

How do I properly configure my Spring Boot application?

Using mongodb with Spring Data MongoDB backend. Using Mongo Repositories too.
This is my actual configuration:
/** MONGO CLIENT *****************************************************/
#Override
protected String getDatabaseName() {
return db;
}
#Override
public Mongo mongo() throws Exception {
/* I'm so dump to automatize this that I just do it manually */
return new Fongo("meh").getMongo(); //Using it for unit tests
//return new MongoClient(url, port); //Using it for IT
}
#Override
protected Collection<String> getMappingBasePackages() {
return Arrays.asList("com.foo");
}
/** BEANS ************************************************************/
#Bean
public Jackson2RepositoryPopulatorFactoryBean repositoryPopulator() {
Resource foo1 = (Resource) new ClassPathResource("collections/foo1.json");
Resource foo2 = (Resource) new ClassPathResource("collections/foo2.json");
Jackson2RepositoryPopulatorFactoryBean factory = new Jackson2RepositoryPopulatorFactoryBean();
factory.setResources(new Resource[] { foo1, foo2 });
return factory;
}
The repository populator is what I added and it's what gives me troubles.
When I compile and test my project I'm getting DuplicateKeyException because I guess the repository populator triggers more than once.
These are the annotations that I use on my test classes:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
Is it well configured my application? What's the reasonable solution to avoid repository populator to trigger multiple times ?
Solution based on this guide (in spanish, sorry): https://www.paradigmadigital.com/dev/tests-integrados-spring-boot-fongo
Is needed to separate fongo configuration from mongo.
fongo configuration must be placed on test/
Just take the example code (and using MongoConfiguration.java too, my actual config is wrong) from the guide as a base and you will be fine.

#EnableMongoAuditing for MongoDB on Cloud Foundry / mongolab

My setup works on my local but not when I deploy it to CloudFoundry/mongolab.
The config is very similar to the docs.
My local spring config
#Configuration
#Profile("dev")
#EnableMongoAuditing
#EnableMongoRepositories(basePackages = "com.foo.model")
public class SpringMongoConfiguration extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "myDb";
}
#Override
public Mongo mongo() throws Exception {
return new MongoClient("localhost");
}
#Bean
public AuditorAware<User> myAuditorProvider() {
return new SpringSecurityAuditorAware();
}
}
This is the cloud foundry setup
#Configuration
#Profile("cloud")
#EnableMongoAuditing
#EnableMongoRepositories(basePackages = "com.foo.model")
public class SpringCloudMongoDBConfiguration extends AbstractMongoConfiguration {
private Cloud getCloud() {
CloudFactory cloudFactory = new CloudFactory();
return cloudFactory.getCloud();
}
#Bean
public MongoDbFactory mongoDbFactory() {
Cloud cloud = getCloud();
MongoServiceInfo serviceInfo = (MongoServiceInfo) cloud.getServiceInfo(cloud.getCloudProperties().getProperty("cloud.services.mongo.id"));
String serviceID = serviceInfo.getId();
return cloud.getServiceConnector(serviceID, MongoDbFactory.class, null);
}
#Override
protected String getDatabaseName() {
Cloud cloud = getCloud();
return cloud.getCloudProperties().getProperty("cloud.services.mongo.id");
}
#Override
public Mongo mongo() throws Exception {
Cloud cloud = getCloud();
return new MongoClient(cloud.getCloudProperties().getProperty("cloud.services.mongo.connection.host"));
}
#Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDbFactory());
}
#Bean
public AuditorAware<User> myAuditorProvider() {
return new SpringSecurityAuditorAware();
}
}
And the error I'm getting when I try to save a document in Cloud Foundry is:
OUT ERROR: org.springframework.data.support.IsNewStrategyFactorySupport - Unexpected error
OUT java.lang.IllegalArgumentException: Unsupported entity com.foo.model.project.Project! Could not determine IsNewStrategy.
OUT at org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:739)
OUT at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
OUT at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
Any ideas? Is it my config file etc..?
Thanks in advance
Niclas
This is usually caused if the Mongo mapping metadata obtained for entities does not scan entities at application startup. By default, AbstractMongoConfiguration uses the package of the actual configuration class to look for #Document annotated classes at startup.
The exception message makes me assume that SpringCloudMongoDBConfiguration is not located in any of the super packages of com.foo.model.project. There are two solutions to this:
Stick to the convenience of putting application configuration classes into the root package of your application. This will cause your application packages be scanned for domain classes, metadata obtained, and the is-new-detection work as expected.
Manually hand the package containing domain classes to the infrastructure by overriding MongoConfiguration.getMappingBasePackage().
The reason you might see the configuration working in the local environment is that the mapping metadata might be obtained through a non-persisting persistence operation (e.g. a query) and everything else proceeding from there.