How to finally and completely disable hibernate in Spring Boot - java

In a Spring Boot project that is not using JPA/Hibernate (but which cannot help but have these libraries on the classpath; long story, but completely unchangeable in this context), I need to stop whatever autoconfiguration is causing the error:
java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
I am aware that I have to explicitly exclude JPA/Hibernate autoconfiguration. I do so in both the #SpringBootApplication-annotated Java class and also in the application.properties file.
#SpringBootApplication
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
#ComponentScan(basePackages = { "com" })
#EnableAsync
public class Application extends SpringBootServletInitializer {
...
}
and
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
Additionally, I've verified that these autoconfigurations are being excluded, by looking in the --debug output:
Exclusions:
-----------
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
Still, it fails when I don't have a JPA #Entity defined.
Why would this be?

Related

How to check if an spring boot application has a certain annotation in autoconfiguration class

I am creating my own spring boot starter to centralise some common configurations in my projects. I would like to let on of my #Configuration classes be only matched, if my spring boot app is annotated in a certain way like so:
#SpringBootApplication
#EnableResourceServer
public class MyApplication {
...
When i use #ConditionalOnClass:
#Configuration
#ConditionalOnClass({EnableResourceServer.class})
class ResourceServerAutoConfiguration
the auto configuration matched when the dependency is used even when the app is not a resource server (annotation is not present).
Is there a condition that only matches when the spring boot app has a certain annotation present?
I believe the simplest way would be to
Add the #Import annotation to your #EnableResourceServer annotation class and pass in your configuration class
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Import(ResourceServerAutoConfiguration.class)
public #interface EnableResourceServer {
}
Remove ResourceServerAutoConfiguration from the auto configuration block of the spring.factories file so it is no longer autoconfigured.
This should result in the config only being loaded when the annotation is present ie.
#SpringBootApplication
#EnableResourceServer
public class NotificationAdapterApplication {
}

Create Custom Repository to Spring Data JPA

I try to create a custom repository by following this tutorial: https://www.baeldung.com/spring-data-jpa-method-in-all-repositories
My app build fail with error:
NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.dao.ExtendedStudentRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
My full source code:
https://github.com/sesong11/springjpa-custom-repo
There are plenty of similar questions, but none of them fixed for me. Perhaps it's Spring issue on current version 2.1.1, or I missed some configuration.
To get your test working, you have to do the following:
1) Replace the incorrect definition of basePackages in StudentJPAH2Config to com.example.demo.dao, or better remove it as redundant:
#Configuration
#EnableJpaRepositories(repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
}
2) Also replace basePackages in #ComponentScan and #EntityScan in DemoApplication class to com.example.demo.dao and com.example.demo.entity. Or better removed those and #EnableTransactionManagement annotations at all:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3) Add a no-argument constructor to the entity Student.
4) Correct your test class - use #DataJpaTest to test DAO layer and import your StudentJPAH2Config configuration:
#RunWith(SpringRunner.class)
#DataJpaTest
#Import(StudentJPAH2Config.class)
public class ExtendedStudentRepositoryIntegrationTest {
//...
}
With these corrections, I've run your test successful.
Before we run the test, let's make sure the main application is running. Actually, it has some issues.
1. IllegalArgumentException: Not a managed type: class com.example.demo.entity.Student.
The problem is #EntityScan("com.example.demo.entity.*"). The package name isn't correct, therefore the Student class isn't scanned.
The solution is #EntityScan("com.example.demo.entity").
2. PropertyReferenceException: No property attributeContainsText found for type Student!
The problem is that the repository base class isn't set, and JpaQueryLookupStrategy thinks it should construct a RepositoryQuery from the method name findByAttributeContainsText. It recognises the pattern findBy{EntityPropertyName} and fails to find the field attributeContainsText in Student.
The repository base class isn't set, because the configuration StudentJPAH2Config isn't applied. A configuration doesn't get applied if it's unable to be scanned.
#ComponentScan("com.example.demo.dao") doesn't scan the package where StudentJPAH2Config resides.
The solution is #ComponentScan("com.example.demo") which will scan both "com.example.demo" and "com.example.demo.dao" packages1.
Now, when the application is fine, let's get back to the test.
3. NoSuchBeanDefinitionException: No qualifying bean of type com.example.demo.dao.ExtendedStudentRepository available.
The problem is that StudentJPAH2Config doesn't constitute the whole configuration, and some beans are missing.
The solution is to list all the configuration classes.
#ContextConfiguration(classes = { StudentJPAH2Config.class, DemoApplication.class })
or
#ContextConfiguration(classes = DemoApplication.class)
or
#SpringBootTest
4. JpaSystemException: No default constructor for entity com.example.demo.entity.Student.
The problem is that Hibernate2 requires the default constructor for Student:
#Entity
public class Student {
#Id
private long id;
private String name;
public Student() {}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
// getters & setters
}
1 #ComponentScan("com.example.demo.dao") is redundant since this package will be scanned because of #SpringBootApplication located in there.
2 Hibernate is the default JPA provider in Spring applications.
Following changes were made:
#SpringBootApplication
#ComponentScan("com.example.demo.dao")
#EntityScan("com.example.demo.entity")
#EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
#EnableTransactionManagement
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
and for StudentJPAH2Config class
#Configuration
#ComponentScan("com.example.demo")
#EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
// additional JPA Configuration
}
Student class which was missing empty constructor:
#Entity
public class Student {
public Student() {
}
public Student(long id, String name) {
this.id = id;
this.name = name;
}
#Id
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And the result
and application running state
Your implementation of the custom Spring Data JPA repository base class does work. There were just a few items with the Spring Boot Application Context that needed to be addressed. You did have Spring Boot Starter modules declared as dependencies, so I submitted a pull request with changes that moved all the responsibility for manually configuring the context to Spring Auto Configuration factory providers.
Here are some notes about changes that were required to get the tests to pass, along with some brief details on each. Note, to run the Spring Boot Application outside of the test scope, you will want to add an appropriate JDBC vendor dependency to the classpath, along with any spring application properties to define the datasource properties. Spring Auto Config should take care of any EntityManagers and TransactionManagers with reasonable settings after the datasource properties are defined. See Spring Boot features - Working with SQL Databases.
Notes on changes:
Removed all Spring Configuration annotations (with exception to #SpringBootApplication and #EnableJpaRepositories). Application pom is configured to depend on Spring Boot Starter modules that provide Spring Auto Configuration classes already through spring.factories while also ensuring all other dependencies are met.
#EnableJpaRepositories does not need to declare basePackages as Auto Config modules will default to using all packages beneath the Spring Main Application class. Declaration of the additional repositoryBaseClass is still necessary.
#ComponentScan removed as Auto Config modules will perform a ComponentScan for all packages beneath the Spring Main Application class, which is already at the top level package for the project. Furthermore, this is declaring a basePackages to scan value that will not include the #Configuration class StudentJPAH2Config.
#EntityScan removed as Auto Config modules will perform a scan in all packages beneath the Spring Main Application class.
#EnableTransactionManagement removed as Auto Config modules will perform the same action.
Removed default application.properties as they depend on H2, however the pom only provides that dependency within the test scope's classpath. Furthermore all configuration properties set within the property file are generally Auto Configured by Spring Starters on presence of H2.
Added a default constructor on Student for hibernate. Hibernate requires this constructor while managing entities, unless #PersistenceConstructor or #Immutable is being used which is outside the scope of this issue.
Configured Spring Boot Auto Configuration for unit test application context in ExtendedStudentRepositoryIntegrationTest. This is the same as the context configuration defined for the Spring Boot integration test in DemoApplicationTests. When using #ContextConfiguration directly with StudentJPAH2Config, the configuration that was defined on the Spring Boot Main Application class (which was the ComponentScan, EntityScan, TransactionManagement) was not being applied to the Spring Test Application Context.
A few notes on Spring Auto Configuration as it was the main issue impeding this project to successfully run:
Spring Application Starters are (generally) split into two types of dependencies.
autoconfigure modules that declare a minimal set of dependencies in it's POM, however are built with many dependencies during it's compilation. The auto configure modules also advertise Configuration classes and factories via a spring.factories file located in META-INF of the package. Spring Boot during it's bootstrapping phase will load these configuration classes. Lastly the configuration classes will selectively declare beans and make other application context changes/enhancements based on other beans declared in the context and what dependencies are actually found on the classpath. This allows for general/reasonable default configurations all based on what libraries you are pulling in and a minimal set of properties.
starter modules (generally) do not provide much/any classes. Their purpose is to provide POMs that declare dependencies required for good baselines of development for a particular Spring project. The starter modules also depend on applicable autoconfigure modules, which pair up with these dependencies to allow you to start running with your application quickly.
For the most part, when using Spring Starters, the main driver should not be figuring out what you need to manually configure in the Spring context, but rather, what you should un-configure in case its baseline does not suit your needs. When it comes to following tutorials/examples online like you have, keep in mind that some items surrounding configuration may be shown. There may be times these configuration instructions are necessary, but often they are to show details for configuration required in non-boot Spring applications or for transparency.
You need an annotation in your implemented class.
You must add the #Component or #Service annotation in order for Spring to pick up the injection
#Component
public class ExtendedRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> {
You need to make below changes to resolve this issue. Also, i executed this with JDK 8 as i don't have JDK 11 in my local. Also, i executed ExtendedStudentRepositoryIntegrationTest.java for the purpose of issue reproduction.
In StudentJPAH2Config.java, component scan need to be added.
#Configuration
#ComponentScan("com.example.demo")
#EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
In DemoApplication the base package name has to be corrected.
#SpringBootApplication
#ComponentScan("com.example.demo.dao")
#EntityScan("com.example.demo.entity")
#EnableTransactionManagement
public class DemoApplication {
The reason for #1 change is without component scan in test config, spring was not able to look for the dependencies in right packages. #2 is needed as your Student.java is inside com.example.demo.entity package directly, not in any subpackage of it. You can also specify #EntityScan in test config file StudentJPAH2Config, though it's not needed.
Now, this resolve the immediate issue faced by you but still you will be welcomed with some other issues which are pretty straight forward and you would be able to take it forward from here.
You have to put #Primaryannotation on the ExtendedRepositoryImpl class. It should work.
Spring basically not able to qualify the dependency. #Primary will make sure wherever you are injecting dependency for ExtendedRepository interface first qualifying bean(class) would be ExtendedRepositoryImpl.
You have to remove #ContextConfiguration(classes = { StudentJPAH2Config.class }) and add #SpringBootTestannotations on your test class ExtendedStudentRepositoryIntegrationTest it will resolve the error NoSuchBeanDefinitionException you currently have.
But again I have got error like:
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.example.demo.entity.Student
which is because Spring is not able to scan Entity Student.
For this you have to change the #EntityScan("com.example.demo.entity.*") to #EntityScan("com.example.demo.entity") on DemoApplication class.
Again I got the error:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property attributeContainsText found for type Student!
Which is because the method findByAttributeContainsText() is not a valid method.

Spring #ComponentScan doesn't work on #Repository

I have a repository in different package than the configuration class , so I annotated it as the following with #Repostiory:
package test;
#Repository
public interface UserTest extends JpaRepository<User, Long> {
}
I have done the component scan on it and it didn't work :
package com.app;
#SpringBootApplication
#ComponentScan({"test","com.app"})
public class Application extends SpringBootServletInitializer {
}
Exception : No qualifying bean of type 'test.UserTest' available: expected at least 1 bean which qualifies as autowire candidate.
why doesn't the component scan work on repository unless I add enableJpaRepositories ? I thought componetScan is enough
Update:
as some of the answers provides solution , I'm asking about explanation not solution . The following will work without even doing component scan on "test" :
SpringBootApplication
#EnableJpaRepositories({"test","com.app"})
public class Application extends SpringBootServletInitializer{
}
Now the question why do I even need to use componentscan on #Repository when it doesn't work ? why in the documentation the #Repository is scanned by componentscan when it doesnt have effect and #EnableJpaRepostiories is enoguh?
from Spring documentation on component scan :
Indicates whether automatic detection of classes annotated with #Component #Repository, #Service, or #Controller should be enabled.
the #Repository in my case is not detected
In order to let spring knows what DataSource is related to what Repository you should define it at the #EnableJpaRepositories annotation.
Try enabling jpa repositories like below.
#SpringBootApplication
#ComponentScan({"test","com.app"})
#EnableJpaRepositories("test")
public class Application extends SpringBootServletInitializer {
}
UPDATE : Why #EnableJpaRepositories needed?
#SpringBootApplication automatically provides the features of the following annotations
#Configuration
#EnableAutoConfiguration
#ComponentScan
But if you try defining your own annotation then spring boot will not take care of internal auto configurations so this is the reason we have to enable repositories.
I have projects in which only #SpringBootApplication is enough if you are not writing your own things.
I hope you got the point.
Golden words :
If you want to get the maximum advantage of spring boot’s auto configuration feature, it is expected to put all your class packages under spring boot main application package (directly in main package or indirectly as sub packages).
I found an explanation about what I was doing wrong. The #Repository annotation with componentscan will not cause spring to implement the spring jpa repository. for the interfaces that implement crud repository enablejparepository should be used.
Now the use of #Repository with componentscan is when you have a repository class and you have your own DAO not for spring curd repo otherwise the implementation won't be created :
#Repository
public class UserTest {
public List<Object> findById(long l) {
.......
}
}
you should use your code like below as #ComponentScan always work with basepackages so your implementation should be like below.
package com.app;
#SpringBootApplication
#ComponentScan(basePackages={"test","com.app"})
#EnableJPARepositories
public class Application extends SpringBootServletInitializer {
}

Disable #EnableAutoConfiguration

I wonder how I can get rid of #EnableAutoConfiguration.
The spring boot documentation mentions that
If you need to find out what auto-configuration is currently being
applied, and why, starting your application with the --debug switch
but I can't find which auto-configurations are applied in the shell.
How does it look like and how do I import / enable just the necessary configurations for my application?
I'm especially interested to just load the spring data autoconfiguration for fast running integration tests.
== Update ==
Adding
#RunWith(SpringJUnit4ClassRunner.class)
#EntityScan("persistence.entities")
#EnableJpaRepositories("persistence.repositories")
#ActiveProfiles("dev")
#Configuration
#Import({ AopAutoConfiguration.class, AopAutoConfiguration.JdkDynamicAutoProxyConfiguration.class,
AuditAutoConfiguration.class, DataSourceAutoConfiguration.class,
DataSourceAutoConfiguration.class, DataSourceAutoConfiguration.class,
DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
JpaBaseConfiguration.class, JpaRepositoriesAutoConfiguration.class,
JtaAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class,
PersistenceExceptionTranslationAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, SpringDataWebAutoConfiguration.class })
#EnableIntegration
#SpringApplicationConfiguration(classes = QueryTests.class)
public class QueryTests {...
to my JUnit test now throws an java.lang.IllegalStateException: Unable to retrieve #EnableAutoConfiguration base packages (see below).
Caused by: java.lang.IllegalStateException: Unable to retrieve #EnableAutoConfiguration base packages
at org.springframework.boot.autoconfigure.AutoConfigurationPackages.get(AutoConfigurationPackages.java:77) ~[spring-boot-autoconfigure-1.2.5.RELEASE.jar:1.2.5.RELEASE]
at org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport.getBasePackages(AbstractRepositoryConfigurationSourceSupport.java:77) ~[spring-boot-autoconfigure-1.2.5.RELEASE.jar:1.2.5.RELEASE] ...
To see the Spring Boot stuff in the logs I add --debug to Program Arguments when I run that my Boot application from the IDEA.
From command line it looks like:
D:\Java\jdk8\bin\java -Didea.launcher.port=7533 "-Didea.launcher.bin.path=D:\IntelliJ IDEA\bin" -Dfile.encoding=windows-1252 -classpath "[SOME CP]" com.intellij.rt.execution.application.AppMain org.springframework.integration.samples.dsl.cafe.lambda.Application --debug
With that I can see in the console something like this:
=========================
AUTO-CONFIGURATION REPORT
=========================
Positive matches:
-----------------
AopAutoConfiguration
- #ConditionalOnClass classes found: org.springframework.context.annotation.EnableAspectJAutoProxy,org.aspectj.lang.annotation.Aspect,org.aspectj.lang.reflect.Advice (OnClassCondition)
- matched (OnPropertyCondition)
AopAutoConfiguration.JdkDynamicAutoProxyConfiguration
- matched (OnPropertyCondition)
GenericCacheConfiguration
- Automatic cache type (CacheCondition)
And so on.
For you second question there are two ways:
Disable undesired AutoConfiguration using #SpringBootApplication(exclude)
Don't rely on the Boot and just import required configurations. For example:
#Configuration
#Import({PropertyPlaceholderAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class, DispatcherServletAutoConfiguration.class})
#EnableIntegration
public static class ContextConfiguration {

Spring boot + Spring security with AspectJ not working

I am trying to configure spring global method security with AspectJ advice mode so I can use #PreAuthorize annotations in all #Configurable annotated classes. This my java configs:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, mode = AdviceMode.ASPECTJ)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration
and:
#EnableCaching
#SpringBootApplication
#EnableSpringConfigured
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
public class WebApplication extends WebMvcConfigurerAdapter
This is my #Configurable class:
#Configurable
public class Entity {
#PreAuthorize("hasPermission(this, 'publish')")
public void method() { }
}
I also added spring-security-aspects as dependency. From AspectJ logs I can clearly see that Spring security related aspects are applied on my #Configurable classes however as soon as I create instance of these classes I get this exception:
Post-processor tried to replace bean instance of type [com.example.Entity] with (proxy) object of type [com.sun.proxy.$Proxy130] - not supported for aspect-configured classes!
I am using spring boot version 1.2.1 therefore spring security version is 3.2.5 . This seems to be bug that was discussed here : Spring Security AspectJMode with #EnableGlobalMethodSecurity not working
However this bug should not affect my version of spring security... Is there any workaround for this issue?
Ok I have solved this. It was problem with spring boot's SecurityAutoConfiguration class. I had to exclude it from auto configuration and configre spring security manually - not very big deal, but anyway...

Categories

Resources