Showing posts with label spring. Show all posts
Showing posts with label spring. Show all posts

Monday, October 8, 2018

Spring secure endpoint with only client credentials (Basic)

Leave a Comment

I have oauth2 authorization server with one custom endpoint (log out specific user manually as admin) I want this endpoint to be secured with rest client credentials (client id and secret as Basic encoded header value), similar to /oauth/check_token.

This endpoint can be called only from my resource server with specific scope.

  1. I need to check if the client is authenticated.
  2. I would like to be able to add @PreAuthorize("#oauth2.hasScope('TEST_SCOPE')")on the controller`s method.

I could not find any docs or way to use the Spring`s mechanism for client authentication check.

EDIT 1

I use java config not an xml one

1 Answers

Answers 1

@PreAuthorize("#oauth2.hasScope('TEST_SCOPE')") On the controller method should be sufficiënt. If the client is not authenticated, no scope is available and the scope check will fail.

If you want, you can use the Spring Security expression @PreAuthorize("isAuthenticated()") to check if a client is authenticated: https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#el-common-built-in

You could also configure the HttpSecurity instead of working with @PreAuthorize

Read More

Saturday, October 6, 2018

Spring Data JPA Meta JpaMetamodelMappingContext Memory Consumption

Leave a Comment

My Spring Data JPA/Hibernate Application consumes over 2GB of memory at start without a single user hitting it. I am using Hazelcast as the second level cache but I had the same issue when I used ehCache as well so that is probably not the cause of the issue.

I ran a profile with a Heap Dump in Visual VM and I see where the bulk of the memory is being consumed by JpaMetamodelMappingContext and secondary a ton of Map objects. I just need help in deciphering what I am seeing and if this is actually a problem. I do have a hundred classes in the model so this may be normal but I have no point of reference. It just seems a bit excessive.

Once I get a load of 100 concurrent users, my memory consumption increases to 6-7 GB. That is quite normal for the amount of data I push around and cache, but I feel like if I could reduce the initial memory, I'd have a lot more room for growth.

Screenshot of Visual VM

enter image description here

1 Answers

Answers 1

I don't think you have a problem here. Instead, I think you are misinterpreting the data you are looking at.

Note that the heap space diagram displays two numbers: Heap size and Used heap

Heap size (orange) is the amount of memory available to the JVM for the heap. This means it is the amount that the JVM requested at some point from the OS.

Used heap is the part of the Heap size that is actually used. Ignoring the startup phase, it grows linear and then drops repeatedly over time. This is typical behavior of an idling application. Some part of the application generates a moderate amount of garbage (rising part of the curve) which from time to time gets collected.

The low points of that curve are the amount of memory you are actually really using. It seems to be about 250MB which doesn't sound very much to me, especially when you say that the total consumption of 6-7GB when actually working sounds reasonable to you.

Some other observations:

Both CPU load and heap grows fast/fluctuates a lot at start time. This is to be expected because the analysis of repositories and entities happen at that time.

JpaMetamodelMappingContext s retained size is about 23MB. Again, a good chunk of memory, but not that huge. This includes the stuff it references, which is almost exclusively metadata from the JPA implementation as you can easily see when you take a look at its source.

Read More

Friday, October 5, 2018

Injecting one of the two @PersistenceContext

Leave a Comment

Consider having two entity manager factories:

<bean id="writeEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">...</bean> <bean id="readOnlyEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">...</bean> 

Then I want to have two Beans to which I would inject the correct persistence context:

<bean id="readOnlyManager" class="..MyDatabaseManager"> <bean id="writeManager" class="..MyDatabaseManager"> 

The bean would look something like:

public class MyDatabaseManager {      private javax.persistence.EntityManager em;      public EntityManager(javax.persistence.EntityManager em) {         this.em = em;     }     ... } 

This obviously doesn't work, because EntityManager is not a bean and cannot be injected in this way:

No qualifying bean of type 'javax.persistence.EntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} 

How can I qualify correct EntityManager in the bean? I used to use @PersistenceContext annotation, but this is not usable as I need to inject it.

How can I specify the PersistenceContext for such Bean?

UPDATE: My question is how to inject PersistenceContext with qualifier via XML, not via annotation.

2 Answers

Answers 1

Assuming that you are using spring in order to manage transactions what I would do is 2 different transaction managers and then in my services i would use the most appropriate transaction manager like this:

Configuration section

@Bean public LocalContainerEntityManagerFactoryBean writeEntityManagerFactory() {      LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();     //Your configuration here     return factory; }  @Bean(name={"writeTx"}) public PlatformTransactionManager writeTransactionManager() {      JpaTransactionManager txManager = new JpaTransactionManager();     txManager.setEntityManagerFactory(writeEntityManagerFactory().getObject());     return txManager; }  @Bean public LocalContainerEntityManagerFactoryBean readEntityManagerFactory() {      LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();     //Your configuration here     return factory; }  @Bean(name={"readTx"}) public PlatformTransactionManager readTransactionManager() {      JpaTransactionManager txManager = new JpaTransactionManager();     txManager.setEntityManagerFactory(readEntityManagerFactory().getObject());     return txManager; } 

Service layer

@Transactional(value="readTx") public List<Object> read(){     //Your read code here }  @Transactional(value="writeTx") public void write(){     //Your write code here } 

UPDATED ANSWER I misunderstood the question.

In your configuration class you can define:

@Bean     public LocalContainerEntityManagerFactoryBean writeEntityManagerFactory() {          LocalContainerEntityManagerFactoryBean em  = new LocalContainerEntityManagerFactoryBean();         em.setDataSource(dataSource());         em.setPackagesToScan(new String[] { "models" });         JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();         em.setJpaVendorAdapter(vendorAdapter);         em.setJpaProperties(hibProps());         em.setPersistenceUnitName("writer");         return em;     }     @Bean     public LocalContainerEntityManagerFactoryBean readEntityManagerFactory() {          LocalContainerEntityManagerFactoryBean em  = new LocalContainerEntityManagerFactoryBean();         em.setDataSource(dataSource());         em.setPackagesToScan(new String[] { "models" });         JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();         em.setJpaVendorAdapter(vendorAdapter);         em.setJpaProperties(hibProps());         em.setPersistenceUnitName("reader");         return em;     }    

Please see the PersistenceUnitName values

Then you can injecting them by doing:

@PersistenceContext(unitName="writer") private EntityManager emWriter;  @PersistenceContext(unitName="reader") private EntityManager emReader; 

I just tested it and all worked pretty good

Angelo

Answers 2

persistence.xml (2 persistence unit for 2 different entitymanager, based on your persistence provider here i ma using HibernatePersistence)

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">    <persistence-unit name="pu1">       <provider>org.hibernate.ejb.HibernatePersistence</provider>    </persistence-unit>     <persistence-unit name="pu2">       <provider>org.hibernate.ejb.HibernatePersistence</provider>    </persistence-unit>    </persistence> 

Make sure you are assigning the persistence-unit to entity manager using property persistenceUnitName

    <bean id="writeEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">             <property name="persistenceXmlLocation" value="classpath: of yout persistence xml" />             <property name="persistenceUnitName" value="pu1" />                .....other configuration of em..       </bean> 

same for other em.

Now, use constructor injection for MyDatabaseManager to inject EntityManager (using qualifier name ex. writeEntityManagerFactory )

<bean id="mdm" class="MyDatabaseManager">      <constructor-arg ref = "writeEntityManagerFactory"/> </bean> 
Read More

Thursday, October 4, 2018

Java/Spring MVC: provide request context to child threads

Leave a Comment

I have the Problem, that I want to outsource some processes of my Spring WebMVC application into separate Threads. That was easy enough and works, until I want to use a class, userRightService, which uses the global request. That's not available in the threads, and we get a problem, that's pretty much understandable.

This is my Error:

java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.userRightsService': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is  java.lang.IllegalStateException: Cannot ask for request attribute -  request is not active anymore! 

Okay, clear enough. I am trying to keep the request context by implementing this solution:

How to enable request scope in async task executor

This is my runnable class:

@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) public class myThread implements Runnable {    private RequestAttributes context;    public DataExportThread(RequestAttributes context) {     this.context = context;   }    public void run() {     RequestContextHolder.setRequestAttributes(context); 

And this where it gets spawned:

final DataExportThread dataExportThread =     new myThread(RequestContextHolder.currentRequestAttributes());  final Thread thread = new Thread(myThread); thread.setUncaughtExceptionHandler((t, e) -> {...}); thread.start(); 

As far as I understood, we store the currentRequestAttributes in the thread and then, when running, we restore them currentRequestAttributes... sounded solid to me, but the error is still there. I think I made some mistake adapting the solution for my case. maybe someone can help me finding the error.

Before I went through a lot of stackoverflow-threads with different solutions (see below), so I could try something else next, but this one seemed the clearest and simplest to me, so I hope someone could help me finding the mistake in the implementation or explain why it's the wrong approach.

I already tried this one without success:

If it's matters:

<org.springframework-version>4.3.4.RELEASE</org.springframework-version> 

BTW: I know that it would be better to restructure the application in a way, that the request is not needed in the thread but that's very complicated in that case and I really hope I could avoid this.

--

Edit1:

The Bean which can not be created in the thread starts like this:

@Service("userRightsService") @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) public class UserRightsService { 

--

Edit2:

I also tried this one:

But context is always empty...

1 Answers

Answers 1

I couldn't reproduce the problem as I am not sure how are you creating/injecting the UserRightsService but I have a couple of suggestions that you may try.

I guess that the problem is that the RequestAttributes is invalidated as the request is over (that's why the exception says Cannot ask for request attribute - request is not active anymore), which happens as your task is running.

Instead, you could try injecting the UserRightsService where your thread is spawned and pass this instance as an argument to the thread. That way the UserRightsService should be created without problem as the request should be still available.

Even so, trying to access the RequestAttributes after the request is over will probably fail. In that case I propose to make a copy of all the values that you need before the request is over, i.e. before your run the thread.

If that doesn't work for you please provide some more info regarding how you initialize the UserRightsService inside the task.

Good luck!

P.S.: I think that the scope annotation in your thread class is useless as the task object is created manually and not managed by spring.

Read More

Monday, September 24, 2018

How to use Query DSL with MongoDB in Spring Boot

Leave a Comment

I try to learn how to use Query DSL with MongoDB in Spring Boot and I get an error. I found this app on youtube, the single difference is that he use the old interface QueryDslPredicateExecutor and I'm using the new one QuerydslPredicateExecutor. The app is working successfully without without using the library for Query DSL for MongoDB. And I want to use this library because I want to use more complex queries. The code should work, I think there is a little mistake somewhere.

The problem is when I click Maven package I get these errors, unfortunatelly I can't post all the output here:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'hotelController' defined in file [C:\Users\dgs\IdeaProjects\springboot-mongodb\target\classes\com\dgs\springbootmongodb\controller\HotelController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hotelRepository': Invocation of init method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel!  Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hotelRepository': Invocation of init method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel!  Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'hotelController' defined in file [C:\Users\dgs\IdeaProjects\springboot-mongodb\target\classes\com\dgs\springbootmongodb\controller\HotelController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hotelRepository': Invocation of init method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel!  Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hotelRepository': Invocation of init method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel!  Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel!  [ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 4.26 s <<< FAILURE! - in com.dgs.springbootmongodb.SpringbootMongodbApplicationTests [ERROR] contextLoads(com.dgs.springbootmongodb.SpringbootMongodbApplicationTests)  Time elapsed: 0.001 s  <<< ERROR! java.lang.IllegalStateException: Failed to load ApplicationContext Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'hotelController' defined in file [C:\Users\dgs\IdeaProjects\springboot-mongodb\target\classes\com\dgs\springbootmongodb\controller\HotelController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hotelRepository': Invocation of init method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel! Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hotelRepository': Invocation of init method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel! Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel! Caused by: java.lang.IllegalArgumentException: Did not find a query class com.dgs.springbootmongodb.models.QHotel for domain class com.dgs.springbootmongodb.models.Hotel! Caused by: java.lang.ClassNotFoundException: com.dgs.springbootmongodb.models.QHotel  [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.21.0:test (default-test) on project springboot-mongodb: There are test failures. 

It is a hotel booking app and this is the code:

The Hotel model:

package com.dgs.springbootmongodb.models;  import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.IndexDirection; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document;  import java.util.ArrayList; import java.util.List;  // The Hotel is the aggregate root so this is the entity on which we will apply our annotations      @Document(collection = "Hotels")     public class Hotel {          @Id         private String id;         private String name;          @Indexed(direction = IndexDirection.ASCENDING)         private int pricePerNight;         private Address address;         private List<Review> reviews;          protected Hotel() {             this.reviews = new ArrayList<>();         }          public Hotel(String name, int pricePerNight, Address address, List<Review> reviews) {             this.name = name;             this.pricePerNight = pricePerNight;             this.address = address;             this.reviews = reviews;         }          public String getId() {             return id;         }          public String getName() {             return name;         }          public int getPricePerNight() {             return pricePerNight;         }          public Address getAddress() {             return address;         }          public List<Review> getReviews() {             return reviews;         }     } 

The HotelRepository interface:

package com.dgs.springbootmongodb.dao;  import com.dgs.springbootmongodb.models.Hotel; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.Query; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.stereotype.Repository;  import java.util.List;  @Repository public interface HotelRepository extends MongoRepository<Hotel, String>, QuerydslPredicateExecutor<Hotel> {      // findBy + PricePerNight (property name) + LessThan (filter)      List<Hotel> findByPricePerNightLessThan(int maxPrice);      @Query(value = "{address.city:?0}")     List<Hotel> findByCity(String city); } 

The HotelController:

package com.dgs.springbootmongodb.controller;  import com.dgs.springbootmongodb.dao.HotelRepository; import com.dgs.springbootmongodb.models.Hotel; import org.springframework.web.bind.annotation.*;  import java.util.List; import java.util.Optional;  @RestController @RequestMapping("/hotels") public class HotelController {      private HotelRepository hotelRepository;      public HotelController(HotelRepository hotelRepository) {         this.hotelRepository = hotelRepository;     }      @GetMapping("/all")     public List<Hotel> getAllHotels() {          List<Hotel> hotels = hotelRepository.findAll();          return hotels;     }      @GetMapping("/{id}")     public Optional<Hotel> getOneHotel(@PathVariable String id) {          return hotelRepository.findById(id);     }      @PostMapping     public Hotel create(@RequestBody Hotel hotel) {          return hotelRepository.save(hotel);     }      @PutMapping     public Hotel update(@RequestBody Hotel hotel) {          return hotelRepository.save(hotel);     }      @DeleteMapping("/delete/{id}")     public List<Hotel> delete(@PathVariable String id) {          hotelRepository.deleteById(id);          return hotelRepository.findAll();     }      @GetMapping("/price/{maxPrice}")     public List<Hotel> getByPricePerNight(@PathVariable int maxPrice) {          List<Hotel> hotels = hotelRepository.findByPricePerNightLessThan(maxPrice);          return hotels;     }      @GetMapping("address/{city}")     public List<Hotel> getByCity(@PathVariable String city) {          List<Hotel> hotels = hotelRepository.findByCity(city);          return hotels;     } } 

This is the pom.xml:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>      <groupId>com.dgs</groupId>     <artifactId>springboot-mongodb</artifactId>     <version>0.0.1-SNAPSHOT</version>     <packaging>jar</packaging>      <name>springboot-mongodb</name>     <description>Demo project for Spring Boot with Mongo DB</description>      <parent>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-parent</artifactId>         <version>2.0.5.RELEASE</version>         <relativePath/> <!-- lookup parent from repository -->     </parent>      <properties>         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>         <java.version>1.8</java.version>     </properties>      <dependencies>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-data-mongodb</artifactId>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>         </dependency>          <!-- Add support for Mongo Query DSL -->          <dependency>             <groupId>com.querydsl</groupId>             <artifactId>querydsl-mongodb</artifactId>             <version>4.1.3</version>             <exclusions>                 <exclusion>                     <groupId>org.mongodb</groupId>                     <artifactId>mongo-java-driver</artifactId>                 </exclusion>             </exclusions>         </dependency>      </dependencies>      <build>         <plugins>             <plugin>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-maven-plugin</artifactId>             </plugin>              <!-- Add plugin for Mongo Query DSL -->              <plugin>                 <groupId>com.mysema.maven</groupId>                 <artifactId>apt-maven-plugin</artifactId>                 <version>1.1.3</version>                 <dependencies>                     <dependency>                         <groupId>com.querydsl</groupId>                         <artifactId>querydsl-apt</artifactId>                         <version>4.1.3</version>                     </dependency>                 </dependencies>                 <executions>                     <execution>                         <phase>generate-sources</phase>                         <goals>                             <goal>process</goal>                         </goals>                         <configuration>                             <outputDirectory>target/generated-sources/annotations</outputDirectory>                             <processor>                                 org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor                             </processor>                             <logOnlyOnError>true</logOnlyOnError>                         </configuration>                     </execution>                 </executions>             </plugin>         </plugins>     </build>   </project> 

Update

This is the starter class:

@SpringBootApplication public class SpringbootMongodbApplication {      public static void main(String[] args) {         SpringApplication.run(SpringbootMongodbApplication.class, args);     } } 

This is the structure of the app:

enter image description here enter image description here

And here is the tutorial from youtube: https://www.youtube.com/watch?v=Hu-cyytqfp8

2 Answers

Answers 1

I managed to make your code work by doing the following:

Change the outputDirectory to <outputDirectory>target/generated-sources</outputDirectory>

Include @Autowired in HotelController

@Autowired private HotelRepository hotelRepository; 

Answers 2

Remove the target folder and start over.

apt-maven-plugin generates, compiles source and adds to classpath. Adjust the plugin's output directory to target/generated-sources/apt.

Run the maven package phase and verify Q class files are inside models packages in targets/classes location and Q source files are inside models package in target/generated-sources/apt.

Read More

Tuesday, September 18, 2018

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'test.spring_session' doesn't exist - Spring Boot

Leave a Comment

I am developing springboot-springsession-jdbc-demo. When I simply run the code I get the following error. It looks to me some property must need to set in application.properties in order create the schema/tables before hand. Required configuration is already in placed and still it gives error. The code is present in https://github.com/sivaprasadreddy/spring-session-samples

The error for reference:

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [DELETE FROM SPRING_SESSION WHERE LAST_ACCESS_TIME < ?]; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'test.spring_session' doesn't exist     at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:231) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:649) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:870) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:931) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:941) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.session.jdbc.JdbcOperationsSessionRepository$6.doInTransaction(JdbcOperationsSessionRepository.java:481) ~[spring-session-1.2.1.RELEASE.jar:na]     at org.springframework.session.jdbc.JdbcOperationsSessionRepository$6.doInTransaction(JdbcOperationsSessionRepository.java:478) ~[spring-session-1.2.1.RELEASE.jar:na]     at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133) ~[spring-tx-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.session.jdbc.JdbcOperationsSessionRepository.cleanUpExpiredSessions(JdbcOperationsSessionRepository.java:478) ~[spring-session-1.2.1.RELEASE.jar:na]     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]     at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]     at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) ~[spring-context-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) [spring-context-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_45]     at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_45]     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_45]     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_45]     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_45]     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_45]     at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45] Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'test.spring_session' doesn't exist     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_45]     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_45]     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_45]     at java.lang.reflect.Constructor.newInstance(Constructor.java:422) ~[na:1.8.0_45]     at com.mysql.jdbc.Util.handleNewInstance(Util.java:404) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.Util.getInstance(Util.java:387) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:942) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3966) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3902) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2526) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2673) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2549) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2073) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2009) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:5098) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1994) ~[mysql-connector-java-5.1.39.jar:5.1.39]     at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:877) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:870) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE]     ... 21 common frames omitted 

pom.xml

    <!-- Parent pom providing dependency and plugin management for applications          built with Maven -->     <parent>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-parent</artifactId>         <version>1.4.0.RELEASE</version>     </parent>      <properties>         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>         <java.version>1.8</java.version>     </properties>       <dependencies>         <!-- Spring Boot Starter Test -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>         </dependency>          <!-- Spring Boot Starter JDBC -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-jdbc</artifactId>         </dependency>          <!-- Spring Boot Starter Web -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>          <!-- Spring Boot Starter Thymeleaf -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-thymeleaf</artifactId>         </dependency>          <!-- Spring Session -->         <dependency>             <groupId>org.springframework.session</groupId>             <artifactId>spring-session</artifactId>         </dependency>           <!-- Spring Boot devtools -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-devtools</artifactId>             <optional>true</optional>         </dependency>          <!-- MYSQL -->         <dependency>             <groupId>mysql</groupId>             <artifactId>mysql-connector-java</artifactId>         </dependency>          <!-- H2 DB -->         <dependency>             <groupId>com.h2database</groupId>             <artifactId>h2</artifactId>         </dependency>     </dependencies> </project> 

Application.java

@SpringBootApplication @EnableJdbcHttpSession @EnableAutoConfiguration public class Application{     public static void main(String[] args){         SpringApplication.run(Application.class, args);     } } 

HomeController.java

@Controller public class HomeController {     private static final AtomicInteger UserId = new AtomicInteger(0);      @RequestMapping("/")     public String home(Model model){         return "index";     }      @RequestMapping("/add-simple-attrs")     public String handleSimpleSessionAttributes(HttpServletRequest req, HttpServletResponse resp){         String attributeName = req.getParameter("attributeName");         String attributeValue = req.getParameter("attributeValue");          req.getSession().setAttribute(attributeName, attributeValue);          User user = new User();         user.setName(attributeValue);          req.getSession().setAttribute(attributeName, user);         return "redirect:/";     }       @RequestMapping("/add-object-attrs")     public String handleObjectSessionAttributes(HttpServletRequest req, HttpServletResponse resp){         String name = req.getParameter("name");         User user = new User();          user.setId(UserId.incrementAndGet());         user.setName(name);          req.getSession().setAttribute("USER_ID_"+user.getId(), user);         return "redirect:/";     } } 

User.java

public class User implements Serializable {     private static final long serialVersionUID = 1L;      private Integer id;     private String name;      public User(){      }      public User(Integer id, String name){         this.id = id;         this.name = name;     }      @Override     public String toString() {         return "User [id=" + id + ", name=" + name + "]";     }      public Integer getId(){         return id;     }      public void setId(Integer id){         this.id = id;     }      public String getName(){         return name;     }      public void setName(String name){         this.name = name;     } } 

application.properties

logging.level.org.springframework=INFO      ################### DataSource Configuration ##########################     spring.datasource.driver-class-name=com.mysql.jdbc.Driver     spring.datasource.url=jdbc:mysql://localhost:3306/test     spring.datasource.username=root     spring.datasource.password=root      spring.datasource.initialize=true     #spring.datasource.data=classpath:org/springframework/session/jdbc/schema-h2.sql     spring.datasource.data=classpath:org/springframework/session/jdbc/schema-mysql.sql     spring.datasource.continue-on-error=true     spring.jpa.hibernate.ddl-auto=create     #spring.jpa.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect 

index.html

 <!DOCTYPE html>     <html xmlns="http://www.w3.org/1999/xhtml"            xmlns:th="http://www.thymeleaf.org">     <head>     <meta charset="utf-8"/>     <title>Home</title>     </head>     <body>         <h2 th:text="#{app.title}">App Title</h2>          <h4>Add Simple Attributes To Session</h4>         <form class="form-inline" role="form" action="add-simple-attrs" method="post">             <label for="attributeName">Attribute Name</label>             <input id="attributeName" type="text" name="attributeName"/>             <label for="attributeValue">Attribute Value</label>             <input id="attributeValue" type="text" name="attributeValue"/>             <input type="submit" value="Set Attribute"/>         </form>          <h4>Add Object Attributes To Session</h4>         <form class="form-inline" role="form" action="add-object-attrs" method="post">             <label for="name">User Name</label>             <input id="name" type="text" name="name"/>             <input type="submit" value="Save"/>         </form>           <h3>Session Attributes</h3>         <table>             <thead>                 <tr>                     <th>Attribute Name</th>                     <th>Attribute Value</th>                 </tr>             </thead>             <tbody>                 <tr th:each="attr : ${session}">                     <td th:text="${attr.key}">Name</td>                     <td th:text="${attr.value}">Value</td>                 </tr>             </tbody>         </table>     </body>     </html> 

2 Answers

Answers 1

I've just had a quite similar error while using spring-boot 2.0.5 (as opposed to 1.4.0 used by OP) with Postgres driver:

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [DELETE FROM spring_sessions WHERE EXPIRY_TIME < ?]; nested exception is org.postgresql.u til.PSQLException: ERROR: relation "spring_sessions" does not exist   Position: 13         at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:234) ~[spring-jdbc-5.0.8.RELEASE.jar!/:5.0.8.RELEA SE]         // redacted...         at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181] Caused by: org.postgresql.util.PSQLException: ERROR: relation "spring_sessions" does not exist   Position: 13         at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2433) ~[postgresql-42.2.2.jar!/:42.2.2]         at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2178) ~[postgresql-42.2.2.jar!/:42.2.2]         // redacted...         at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:605) ~[spring-jdbc-5.0.8.RELEASE.jar!/:5.0.8.RELEASE]         ... 16 common frames omitted 

According to documentation, setting up Spring Session backed by a relational database is as simple as adding a single configuration property to your application.properties:

spring.session.store-type=jdbc 

Note that in order to get the session tables auto-created, I had to also specify:

spring.session.jdbc.initialize-schema=always 

Once this setting specified, Spring used the correct SQL initialization script from spring-session-jdbc jar. My mistake was not specifying that option - in which case embedded was being used as default value.

Answers 2

I think you have multiple issues By default jdbc has H2 database. It is automatically created by spring. Check if it is there. So first run it on H2. Then copy the same database and table to MySQL. create same schema and table to MySQL then change connection to MySQL.

Spring Boot automatically creates a DataSource that connects Spring Session to an embedded instance of H2 database src/main/resources/application.properties

spring.datasource.url= # JDBC URL of the database. spring.datasource.username= # Login username of the database. spring.datasource.password= # Login password of the database.  spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=root 
Read More

Thursday, September 13, 2018

hibernate second level cache with Redis -will it improve performance?

Leave a Comment

I am currently developing application using Spring MVC4 and hibernate 4 .I have implemented hibernate second level cache for performance improvement .If I use Redis which is in-memory data structure store, used as a database, cache etc, performance will increase but will it be a drastic change?

4 Answers

Answers 1

Drastic differences you may expect if you cache what is good to be cached and avoid caching data that should not be cached at all. Like beauty is in the eye of the beholder same is with the performance. Here are several aspects you should have in mind when using hibernate AS second level cache provider:

No Custom serialization - Memory intensive
If you use second level caching you would not be able to use fast serialization frameworks as Kryo and will have to stick to java serializeable which sucks.

On top of this for each entity type you will have a separate region and within each region you will have entry for each key of each entity. In terms of memory efficiency this is inefficient.

Lacks ability to store and distribute rich objects
Most of the modern caches also present computing grid functionality having your objects fragmented into many small pieces decrease your ability ability to execute distributed tasks with guaranteed data co-location. That depends a little bit on the Grid provider, but for many would be limitation.

Sub optimal performance
Depending on how much performance you need and what type of application you are having using hibernate second level cache might be a good or a bad choice. Good in terms that it is plug and play...."kind of..." bad because you will never squeeze the performance you would have gained. Also designing rich models mean more upfront work and more OOP.

Limited querying capabilities ON the Cache itself
That depends on the cache provider , but some of the provider really are not good doing JOINs with Where clause different than the ID. If you try to build and in memory index for a query on Hazelcast for example you will see what I mean.

Answers 2

Yes, if you use Redis, it will improve your performance.

No, it will not be a drastic change. :)

https://memorynotfound.com/spring-redis-application-configuration-example/

http://www.baeldung.com/spring-data-redis-tutorial

the above links will help you to find out the way of integration redis with your project.

Answers 3

Your question was already discussed here. Check this link: Application cache v.s. hibernate second level cache, which to use?

This was the most accepted answer, which I agree with:

It really depends on your application querying model and the traffic demands.

  1. Using Redis/Hazelcast may yield the best performance since there won't be any round-trip to DB anymore, but you end up having a normalized data in DB and denormalized copy in you cache which will put pressure on your cache update policies. So you gain the best performance at the cost of implementing the cache update whenever the persisted data changes.
  2. Using 2nd level cache is easier to setup but it only stores entities by id. There is also a query cache, storing ids returned by a given query. So the 2nd level cache is a two step process that you need to fine tune to get the best performance. When you execute projection queries the 2nd level object cache won't help you, since it only operates on entity load. The main advantage of 2nd level cache is that it's easier to keep it in sync whenever data changes, especially if all your data is persisted by hibernate.

So, if you need ultimate performance and you don't mind implementing your cache update logic that ensures a minimum eventual consistency window, then go with an external cache.

If you only need to cache entities (that usually don't change that frequently) and you mostly access those through Hibernate entity loading, then 2nd level cache can help you.

Hope it helps!

Answers 4

It depends on the movement.

If You have 1000 or more requests per second and You are low on RAM, then Yes, use redis nodes on other machine to take some usage. It will greatly improve your RAM and request speed.

But If it's otherwise then do not use it.

Remember that You can use this approach later when You will see what is the RAM and database Connection Pool usage.

Read More

Saturday, September 8, 2018

Using multiple OAuth2 clients in single browser session using Spring boot

Leave a Comment

We have Multi tenant WebApp designed using Spring Boot + Spring Security. This app is used to manage certain resources in Azure. User login into our WebApp using OAuth2.0 and can access Azure resources through our app.

Now we need to allow multiple users to login into our app in single browser session. So basically user (user 1) will use credentials1 to login to access resources allowed by these credentials. Then user will use credentials2 (basically another users credentials lets call it user2) to login into same browser page. There will be two active users in same session. User should be able to switch between these accounts.

Once user login into our app, we instantiate RestTemplate (using credentials entered) to access Azure resources.

Either we can have single JSession id mapped to multiple RestTemplate or multiple JSession ID (within single JSession cookie) to mapped to individual RestTemplate. We can have request parameter indicating which RestTemplate to use.

We have used SpringSecurity to get access token. This access token is then used in RestTemplate and used for accessing Azure resources.

1 Answers

Answers 1

"Now we need to allow multiple users to login into our app in single browser session"

Is this approach secure, at all? I mean, having two users using the same browser and sharing information isn't recommended.

"Either we can have single JSession id mapped to multiple RestTemplate or multiple JSession ID (within single JSession cookie) to mapped to individual RestTemplate"

I never saw this kind of approach. Get Google as an example -- you can switch profiles, but need to log in.

If you really need to do it, there's an out of the box solution for Chrome, Firefox and Opera called SessionBox, that enables session switch within the same browser. Otherwise, two common solutions are:

  • Use two different browsers (e.g. Chrome and Firefox)
  • Use incognito mode
Read More

Tuesday, September 4, 2018

No qualifying bean of type 'org.springframework.cloud.bootstrap.encrypt.RsaProperties'

Leave a Comment

I am getting a nosuchbean exception as the title suggests, just adding text here to satisfy the mostly code thing.

Have put unlimited crypto jars in jre\lib\security

Key store created in application at src\main\resources and is called config-server.jks

application.properties (tried both key-stores location prop definitions)

server.port=8888 spring.cloud.config.server.git.uri=ssh://git@v00bitbucket:7999/proj/config-server.git spring.cloud.config.server.git.clone-on-start=true security.user.name=Joe security.user.password={bcrypt}$2a$10$7H8tnjyf/Mn90eAZADruterXJ.t.GQP4WgRIZ8cwnRsMmhZhCtS1a #encrypt.key-store.location=classpath:/config-server.jks encrypt.key-store.location=file://C:/myAppDir/config- server/src/main/resources/config-server.jks encrypt.key-store.password=my-s70r3-s3cr3t encrypt.key-store.alias=config-server-key encrypt.key-store.secret=my-k34-s3cr3t 

using java 1.8.0_77

@RunWith(SpringRunner.class) @SpringBootTest public class ConfigServerApplicationTests {         @Test     public void contextLoads() {     } }       @SpringBootApplication @EnableConfigServer public class ConfigServerApplication {     public static void main(String[] args) {         SpringApplication.run(ConfigServerApplication.class, args);     } }  @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter {     @Value("${security.user.name}")     private String authUser;     @Value("${security.user.password}")     private String authPassword; // this password is encoded     @Autowired     protected void configure(AuthenticationManagerBuilder auth) throws Exception {         auth.inMemoryAuthentication().passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder())         .withUser(authUser).password(authPassword).roles("User");     }      @Override     protected void configure(HttpSecurity http) throws Exception {         http.authorizeRequests().anyRequest().fullyAuthenticated();         http.httpBasic();         http.csrf().disable();     } } 

here is the pom {

<parent>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-parent</artifactId>     <version>2.0.4.RELEASE</version>     <relativePath/> <!-- lookup parent from repository --> </parent>  <properties>     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>     <java.version>1.8</java.version>     <spring-cloud.version>Finchley.SR1</spring-cloud.version> </properties>  <dependencies>     <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-web</artifactId>     </dependency>     <dependency>         <groupId>org.springframework.cloud</groupId>         <artifactId>spring-cloud-config-server</artifactId>     </dependency>      <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-tomcat</artifactId>         <scope>provided</scope>     </dependency>     <dependency>         <groupId>org.springframework.security</groupId>         <artifactId>spring-security-config</artifactId>     </dependency>     <dependency>         <groupId>org.springframework.security</groupId>         <artifactId>spring-security-web</artifactId>     </dependency>     <dependency>         <groupId>org.springframework.security</groupId>         <artifactId>spring-security-rsa</artifactId>     </dependency>     <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-test</artifactId>         <scope>test</scope>     </dependency> </dependencies> 

}

1 Answers

Answers 1

Just ran into this problem too.

My best guess is that it is a bug in the latest version of Spring Cloud. I will open an issue on Spring Cloud project and link it here once finished.

I am using application.yml, not application.properties.

When you put any config for encrypt: * in application yml, it will give you this error. As a work-a-round, I tried putting the encrypt:* config in bootstrap.yml

After that, the Spring Boot app started successfully and it will have the RsaProperties Bean :)

Hope this helps!

Read More

How to specify external location for image store in Spring Boot 2.0 web app?

Leave a Comment

Currently I have images stored in /src/main/resources/static/myimages in the project directory. Now I want to move these outside of project directory like in /Users/tom/myimages so that in img tag src="/myimages/subdir/first.jpg" in the HTML markup will be loaded from /Users/tom/myimages/subdir/first.jpg. How can I achieve this in spring boot 2.0 project?

This will allow me to add new images without having to recompile the project in production environment.

3 Answers

Answers 1

You could achieve this, by PathResourceResolver which is the simplest resolver and its purpose is to find a resource given a public URL pattern. In fact, this is the default resolve.

Code:

@Configuration @EnableWebMvc public class MvcConfig implements WebMvcConfigurer {     @Override     public void addResourceHandlers(ResourceHandlerRegistry registry) {        registry                .addResourceHandler("/myimages/**")                .addResourceLocations("/Users/tom/myimages")                .setCachePeriod(3600)                .resourceChain(true)                .addResolver(new PathResourceResolver());     } }  

Description:

  • We are registering the PathResourceResolver in the resource chain as the sole ResourceResolver in it.
  • the html code that, in conjunction with the PathResourceResolver, locates the /first.jpg file in the /Users/tom/myimages folder

Answers 2

May be you should try to define your own static resource handler ?

It will override the default one.

Something like this:

@Configuration  public class StaticResourceConfiguration extends WebMvcConfigurerAdapter {      @Override      public void addResourceHandlers(ResourceHandlerRegistry registry) {          registry.addResourceHandler("/**").addResourceLocations("file:/path/to/my/folder/");      }  }

UPDATE That one is something seems to be work on some of my old projects:

@Configuration  @AutoConfigureAfter(DispatcherServletAutoConfiguration.class)  public class CustomWebMvcAutoConfig extends                      WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter {      @Override    public void addResourceHandlers(ResourceHandlerRegistry registry) {      String myExternalFilePath = "file:///C:/Users/tom/imgs/";        registry.addResourceHandler("/imgs/**").addResourceLocations(myExternalFilePath);        super.addResourceHandlers(registry);    }    }

Answers 3

Spring 5 - Static Resources

From the documentation:

@Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer {          public void addResourceHandlers (ResourceHandlerRegistry registry) {             registry.addResourceHandler("/pages/**").                       addResourceLocations("classpath:/my-custom-location/","C:/spark/Hadoop/my-custom-location/");           } } 

or You can store Image path in Database. Load it in dynamically when ever required also you can change that path on fly.

HTML Code Will be like this :-

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">   <link href='<spring:url value="/resources/css/style.css"/>' rel="stylesheet" /> <script type="text/javascript" src='<spring:url value="/resources/js/app.js"/>'></script>  </head> <body>    <h1 id="title" class="color1">Spring MVC- Static Resource Mapping Example</h1>     <button onclick="changeColor()">Change Color</button>    <hr />     <img alt="http://mytechnologythought.blogspot.com" src="<spring:url value="/pages/img01.png"/>" width="200"> </body> </html> 

Here Full Example Reference Blog link Note :- The type WebMvcConfigurerAdapter is deprecated

Read More

Friday, August 31, 2018

How to migrate existing Spring project to Spring Boot

Leave a Comment

I'm trying to migrate existing Spring project to Spring Boot. In project already used Spring Data JPA/Hibernate and simple DAO with JDBC (PostgreSQL used). In few states I found that all that I need to migrate on Spring boot, is:

  1. Add necessary dependencies
  2. Add entry point @SpringBootApplication
  3. profit, that's all.

1) Dependencies:

<dependencyManagement>         <dependencies>             <dependency>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-dependencies</artifactId>                 <version>${spring.boot.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>          </dependencies>     </dependencyManagement> <dependency>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-test</artifactId>         <scope>test</scope>     </dependency>     <dependency>         <groupId>com.fasterxml.jackson.core</groupId>         <artifactId>jackson-databind</artifactId>         <version>2.9.4</version>     </dependency> <dependency>             <groupId>org.springframework.data</groupId>             <artifactId>spring-data-jpa</artifactId>             <version>2.0.5.RELEASE</version>         </dependency> <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-data-jpa</artifactId>             <version>1.4.7.RELEASE</version>         </dependency> <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-jetty</artifactId>             <version>1.4.7.RELEASE</version>         </dependency> 

With dependencyManagment section I have error:

Exception in thread "main" java.lang.NoSuchMethodError: org.springframework.data.repository.config.RepositoryConfigurationSource.getAttribute(Ljava/lang/String;)Ljava/util/Optional;     at org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension.postProcess(JpaRepositoryConfigExtension.java:125)     at org.springframework.data.repository.config.RepositoryConfigurationDelegate.registerRepositoriesIn(RepositoryConfigurationDelegate.java:127)     at org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport.registerBeanDefinitions(RepositoryBeanDefinitionRegistrarSupport.java:83)     at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:359)     at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:143)     at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:116)     at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:320)     at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228)     at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:272)     at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:92)     at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687)     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525)     at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)     at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)     at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)     at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)     at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)     at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)     at ru.testproject.BootConfiguration.main(BootConfiguration.java:26) 

And without it:

Exception in thread "main" org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is java.lang.NoClassDefFoundError: org/eclipse/jetty/util/DeprecationWarning     at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:137)     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)     at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)     at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)     at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)     at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)     at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)     at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)     at ru.testproject.BootConfiguration.main(BootConfiguration.java:26) Caused by: java.lang.NoClassDefFoundError: org/eclipse/jetty/util/DeprecationWarning     at org.eclipse.jetty.servlet.ServletContextHandler.<init>(ServletContextHandler.java:159)     at org.eclipse.jetty.webapp.WebAppContext.<init>(WebAppContext.java:289)     at org.eclipse.jetty.webapp.WebAppContext.<init>(WebAppContext.java:211)     at org.springframework.boot.context.embedded.jetty.JettyEmbeddedWebAppContext.<init>(JettyEmbeddedWebAppContext.java:28)     at org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory.getEmbeddedServletContainer(JettyEmbeddedServletContainerFactory.java:170)     at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:164)     at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:134)     ... 8 more Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.util.DeprecationWarning     at java.net.URLClassLoader.findClass(URLClassLoader.java:381)     at java.lang.ClassLoader.loadClass(ClassLoader.java:424)     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)     at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 

2) entry point (I'm also tried to Import configuration classes, commented):

@SpringBootApplication //@Import({DatabaseConfig.class, WebMvcConfig.class, WebAppConfig.class, WebSecurityConfig.class, WebServiceConfig.class}) public class BootConfiguration extends SpringBootServletInitializer {      public static void main(String[] args) {         SpringApplication.run(BootConfiguration.class, args); //        SpringApplication.run(new Class<?>[] {BootConfiguration.class, DatabaseConfig.class, WebMvcConfig.class, WebAppConfig.class, WebSecurityConfig.class, WebServiceConfig.class}, args);     }      @Bean     public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {         JettyEmbeddedServletContainerFactory jettyContainer =                 new JettyEmbeddedServletContainerFactory();          jettyContainer.setPort(9000);         jettyContainer.setContextPath("");         return jettyContainer;     }      @Override     protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {         return application.sources(BootConfiguration.class);     } } 

And configuration:

@Configuration @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) @EnableJpaRepositories(basePackages = {         "ru.testproject.hibernate" }) @EnableTransactionManagement @PropertySource("classpath:application.properties") public class DatabaseConfig implements TransactionManagementConfigurer {      @Bean     public DataSource dataSource() {          if (DB_TYPE_POSTGRESQL.equalsIgnoreCase(dbType)) {             return postresqlDataSource();         } else {             return h2DataSource();         }      }      @Bean     public PlatformTransactionManager annotationDrivenTransactionManager() {         return new JpaTransactionManager();     } } 

I have no idea what I'm doing wrong. What do I need to do to start spring boot application with existing Jetty server configuration?

UPDATE I've modified the main pom:

<parent>         <groupId>ru.testproject</groupId>         <artifactId>test</artifactId>         <version>1.0.1</version>     </parent>  <dependencyManagement>         <dependencies>             <dependency>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-dependencies</artifactId>                 <version>${spring.boot.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>          </dependencies>     </dependencyManagement> ... <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-data-jpa</artifactId>         </dependency>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-jetty</artifactId>         </dependency> <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web-services</artifactId>             <!-- <version>${spring.boot.version}</version> -->             <exclusions>                 <exclusion>                     <groupId>org.springframework.boot</groupId>                     <artifactId>spring-boot-starter-tomcat</artifactId>                 </exclusion>                  <exclusion>                     <groupId>org.springframework.boot</groupId>                     <artifactId>spring-boot-starter-logging</artifactId>                 </exclusion>                 <exclusion>                     <groupId>org.springframework.boot</groupId>                     <artifactId>spring-boot-starter-validation</artifactId>                 </exclusion>                 <exclusion>                     <groupId>org.jboss.logging</groupId>                     <artifactId>jboss-logging</artifactId>                 </exclusion>                 <exclusion>                     <groupId>com.fasterxml.jackson.core</groupId>                     <artifactId>jackson-core</artifactId>                 </exclusion>                 <exclusion>                     <groupId>com.fasterxml.jackson.core</groupId>                     <artifactId>jackson-databind</artifactId>                 </exclusion>                 <exclusion>                     <groupId>com.fasterxml.jackson.core</groupId>                     <artifactId>jackson-annotations</artifactId>                 </exclusion>                 <exclusion>                     <groupId>org.hibernate</groupId>                     <artifactId>hibernate-validator</artifactId>                 </exclusion>              </exclusions>         </dependency>  <!-- Hibernate -->         <dependency>             <groupId>org.hibernate</groupId>             <artifactId>hibernate-entitymanager</artifactId>             <version>${hibernate-version}</version>         </dependency>         <dependency>             <groupId>org.hibernate</groupId>             <artifactId>hibernate-core</artifactId>             <version>${hibernate-version}</version>         </dependency>         <dependency>             <groupId>org.hibernate.javax.persistence</groupId>             <artifactId>hibernate-jpa-2.0-api</artifactId>             <version>1.0.1.Final</version>         </dependency>  <dependency>             <groupId>org.springframework</groupId>             <artifactId>spring-webmvc</artifactId>             <version>${spring.version}</version>         </dependency>          <dependency>             <groupId>org.springframework.security</groupId>             <artifactId>spring-security-config</artifactId>             <version>4.2.1.RELEASE</version>         </dependency>          <dependency>             <groupId>org.springframework.security</groupId>             <artifactId>spring-security-web</artifactId>             <version>4.2.0.RELEASE</version>         </dependency>          <dependency>             <groupId>org.springframework.security</groupId>             <artifactId>spring-security-taglibs</artifactId>             <version>4.2.0.RELEASE</version>         </dependency>          <dependency>             <groupId>org.springframework</groupId>             <artifactId>spring-jdbc</artifactId>             <version>${spring.version}</version>         </dependency> <!-- Version 5.0.4 because ${spring.version} context fails with CandidateComponentsIndexLoader error (it introduced in 5.0.0 version) -->         <dependency>             <groupId>org.springframework</groupId>             <artifactId>spring-context</artifactId>             <version>5.0.4.RELEASE</version>         </dependency>         <dependency>             <groupId>org.springframework</groupId>             <artifactId>spring-context-support</artifactId>             <version>${spring.version}</version>         </dependency>         <dependency>             <groupId>org.springframework</groupId>             <artifactId>spring-tx</artifactId>             <version>${spring.version}</version>         </dependency>         <dependency>             <groupId>javax.xml.bind</groupId>             <artifactId>jaxb-api</artifactId>             <version>2.3.0</version>         </dependency>          <!-- Jetty embedded -->         <dependency>             <groupId>javax.servlet</groupId>             <artifactId>javax.servlet-api</artifactId>             <version>3.1.0</version>         </dependency> 

also, I've commented all tags for spring boot dependencies. mvc dependency:tree give the following output:

[INFO] --- maven-dependency-plugin:3.0.2:tree (default-cli) @ test --- [WARNING] The artifact org.hibernate:hibernate-infinispan:jar:5.3.3.Final has been relocated to org.infinispan:infinispan-hibernate-cache-v53:jar:9.3.0.Final [INFO] ru.testproject:test:jar:2.4.41-SNAPSHOT [INFO] +- org.springframework.boot:spring-boot-starter-test:jar:1.5.8.RELEASE:test [INFO] |  +- org.springframework.boot:spring-boot-test:jar:1.5.8.RELEASE:test [INFO] |  |  \- org.springframework.boot:spring-boot:jar:1.5.8.RELEASE:compile [INFO] |  +- org.springframework.boot:spring-boot-test-autoconfigure:jar:1.5.8.RELEASE:test [INFO] |  |  \- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.8.RELEASE:compile [INFO] |  +- com.jayway.jsonpath:json-path:jar:2.2.0:test [INFO] |  |  \- net.minidev:json-smart:jar:2.2.1:test [INFO] |  |     \- net.minidev:accessors-smart:jar:1.1:test [INFO] |  +- org.assertj:assertj-core:jar:2.6.0:test [INFO] |  +- org.mockito:mockito-core:jar:1.10.19:test [INFO] |  |  \- org.objenesis:objenesis:jar:2.1:test [INFO] |  +- org.hamcrest:hamcrest-core:jar:1.3:test [INFO] |  +- org.hamcrest:hamcrest-library:jar:1.3:test [INFO] |  +- org.skyscreamer:jsonassert:jar:1.4.0:test [INFO] |  |  \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test [INFO] |  +- org.springframework:spring-core:jar:4.3.12.RELEASE:compile [INFO] |  \- org.springframework:spring-test:jar:4.3.12.RELEASE:test [INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:1.5.8.RELEASE:compile [INFO] |  +- org.springframework.boot:spring-boot-starter:jar:1.5.8.RELEASE:compile [INFO] |  |  +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.8.RELEASE:compile [INFO] |  |  |  +- ch.qos.logback:logback-classic:jar:1.1.11:compile [INFO] |  |  |  |  \- ch.qos.logback:logback-core:jar:1.1.11:compile [INFO] |  |  |  +- org.slf4j:jul-to-slf4j:jar:1.7.25:compile [INFO] |  |  |  \- org.slf4j:log4j-over-slf4j:jar:1.7.25:compile [INFO] |  |  \- org.yaml:snakeyaml:jar:1.17:runtime [INFO] |  +- org.springframework.boot:spring-boot-starter-aop:jar:1.5.8.RELEASE:compile [INFO] |  |  \- org.aspectj:aspectjweaver:jar:1.8.11:compile [INFO] |  +- org.springframework.boot:spring-boot-starter-jdbc:jar:1.5.8.RELEASE:compile [INFO] |  |  \- org.apache.tomcat:tomcat-jdbc:jar:8.5.23:compile [INFO] |  |     \- org.apache.tomcat:tomcat-juli:jar:8.5.23:compile [INFO] |  +- javax.transaction:javax.transaction-api:jar:1.2:compile [INFO] |  +- org.springframework.data:spring-data-jpa:jar:1.11.8.RELEASE:compile [INFO] |  |  +- org.springframework.data:spring-data-commons:jar:1.13.8.RELEASE:compile [INFO] |  |  +- org.springframework:spring-orm:jar:4.3.12.RELEASE:compile [INFO] |  |  \- org.slf4j:jcl-over-slf4j:jar:1.7.25:compile [INFO] |  \- org.springframework:spring-aspects:jar:4.3.12.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot-starter-jetty:jar:1.5.8.RELEASE:compile [INFO] |  +- org.eclipse.jetty:jetty-webapp:jar:9.4.7.v20170914:compile [INFO] |  |  +- org.eclipse.jetty:jetty-xml:jar:9.4.7.v20170914:compile [INFO] |  |  \- org.eclipse.jetty:jetty-servlet:jar:9.4.7.v20170914:compile [INFO] |  |     \- org.eclipse.jetty:jetty-security:jar:9.4.7.v20170914:compile [INFO] |  |        \- org.eclipse.jetty:jetty-server:jar:9.4.7.v20170914:compile [INFO] |  +- org.eclipse.jetty.websocket:websocket-server:jar:9.4.7.v20170914:compile [INFO] |  |  +- org.eclipse.jetty.websocket:websocket-common:jar:9.4.7.v20170914:compile [INFO] |  |  |  \- org.eclipse.jetty.websocket:websocket-api:jar:9.4.7.v20170914:compile [INFO] |  |  +- org.eclipse.jetty.websocket:websocket-client:jar:9.4.7.v20170914:compile [INFO] |  |  |  \- org.eclipse.jetty:jetty-client:jar:9.4.7.v20170914:compile [INFO] |  |  \- org.eclipse.jetty.websocket:websocket-servlet:jar:9.4.7.v20170914:compile [INFO] |  +- org.eclipse.jetty.websocket:javax-websocket-server-impl:jar:9.4.7.v20170914:compile [INFO] |  |  +- org.eclipse.jetty:jetty-annotations:jar:9.4.7.v20170914:compile [INFO] |  |  |  +- org.eclipse.jetty:jetty-plus:jar:9.4.7.v20170914:compile [INFO] |  |  |  +- javax.annotation:javax.annotation-api:jar:1.2:compile [INFO] |  |  |  +- org.ow2.asm:asm:jar:5.1:compile [INFO] |  |  |  \- org.ow2.asm:asm-commons:jar:5.1:compile [INFO] |  |  |     \- org.ow2.asm:asm-tree:jar:5.1:compile [INFO] |  |  +- org.eclipse.jetty.websocket:javax-websocket-client-impl:jar:9.4.7.v20170914:compile [INFO] |  |  \- javax.websocket:javax.websocket-api:jar:1.0:compile [INFO] |  \- org.mortbay.jasper:apache-el:jar:8.0.33:compile [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.1:compile [INFO] +- org.springframework.boot:spring-boot-starter-web-services:jar:1.5.8.RELEASE:compile [INFO] |  +- org.springframework.boot:spring-boot-starter-web:jar:1.5.8.RELEASE:compile [INFO] |  +- org.springframework:spring-oxm:jar:4.3.12.RELEASE:compile [INFO] |  \- org.springframework.ws:spring-ws-core:jar:2.4.0.RELEASE:compile [INFO] |     \- org.springframework.ws:spring-xml:jar:2.4.0.RELEASE:compile [INFO] +- org.hibernate:hibernate-entitymanager:jar:5.3.3.Final:compile [INFO] |  +- org.jboss.logging:jboss-logging:jar:3.3.1.Final:compile [INFO] |  +- dom4j:dom4j:jar:1.6.1:compile [INFO] |  +- org.hibernate.common:hibernate-commons-annotations:jar:5.0.4.Final:compile [INFO] |  +- javax.persistence:javax.persistence-api:jar:2.2:compile [INFO] |  +- net.bytebuddy:byte-buddy:jar:1.8.13:compile [INFO] |  \- org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:jar:1.1.1.Final:compile [INFO] +- org.hibernate:hibernate-core:jar:5.3.3.Final:compile [INFO] |  +- org.javassist:javassist:jar:3.21.0-GA:compile [INFO] |  +- antlr:antlr:jar:2.7.7:compile [INFO] |  +- org.jboss:jandex:jar:2.0.5.Final:compile [INFO] |  +- com.fasterxml:classmate:jar:1.3.4:compile [INFO] |  \- javax.activation:javax.activation-api:jar:1.2.0:compile [INFO] +- org.hibernate.javax.persistence:hibernate-jpa-2.0-api:jar:1.0.1.Final:compile [INFO] +- org.hibernate:hibernate-osgi:jar:5.3.3.Final:compile [INFO] |  +- javax.interceptor:javax.interceptor-api:jar:1.2:compile [INFO] |  +- org.osgi:org.osgi.core:jar:6.0.0:compile [INFO] |  \- org.osgi:org.osgi.compendium:jar:5.0.0:compile [INFO] +- org.hibernate:hibernate-envers:jar:5.3.3.Final:compile [INFO] +- org.hibernate:hibernate-hikaricp:jar:5.3.3.Final:compile [INFO] +- org.hibernate:hibernate-proxool:jar:5.3.3.Final:compile [INFO] |  \- proxool:proxool:jar:0.8.3:compile [INFO] +- org.infinispan:infinispan-hibernate-cache-v53:jar:9.3.0.Final:compile [INFO] |  +- org.infinispan:infinispan-hibernate-cache-commons:jar:9.3.0.Final:compile [INFO] |  +- org.infinispan:infinispan-hibernate-cache-spi:jar:9.3.0.Final:compile [INFO] |  \- org.infinispan:infinispan-core:jar:9.3.0.Final:compile [INFO] |     +- org.infinispan:infinispan-commons:jar:9.3.0.Final:compile [INFO] |     +- org.jgroups:jgroups:jar:4.0.12.Final:compile [INFO] |     +- com.github.ben-manes.caffeine:caffeine:jar:2.3.5:compile [INFO] |     +- org.jboss.spec.javax.transaction:jboss-transaction-api_1.1_spec:jar:1.0.1.Final:compile [INFO] |     +- org.jboss.marshalling:jboss-marshalling-osgi:jar:2.0.5.Final:compile [INFO] |     \- io.reactivex.rxjava2:rxjava:jar:2.1.3:compile [INFO] |        \- org.reactivestreams:reactive-streams:jar:1.0.1:compile [INFO] +- org.hibernate:hibernate-ehcache:jar:5.3.3.Final:compile [INFO] |  \- net.sf.ehcache:ehcache:jar:2.10.4:compile [INFO] +- wsdl4j:wsdl4j:jar:1.6.1:compile [INFO] +- org.springframework:spring-webmvc:jar:4.3.12.RELEASE:compile [INFO] |  +- org.springframework:spring-aop:jar:4.3.12.RELEASE:compile [INFO] |  +- org.springframework:spring-beans:jar:4.3.12.RELEASE:compile [INFO] |  +- org.springframework:spring-expression:jar:4.3.12.RELEASE:compile [INFO] |  \- org.springframework:spring-web:jar:4.3.12.RELEASE:compile [INFO] +- org.springframework.security:spring-security-config:jar:4.2.1.RELEASE:compile [INFO] |  +- aopalliance:aopalliance:jar:1.0:compile [INFO] |  \- org.springframework.security:spring-security-core:jar:4.2.3.RELEASE:compile [INFO] +- org.springframework.security:spring-security-web:jar:4.2.0.RELEASE:compile [INFO] +- org.springframework.security:spring-security-taglibs:jar:4.2.0.RELEASE:compile [INFO] |  \- org.springframework.security:spring-security-acl:jar:4.2.3.RELEASE:compile [INFO] +- org.springframework:spring-jdbc:jar:4.3.12.RELEASE:compile [INFO] +- org.springframework:spring-context:jar:5.0.4.RELEASE:compile [INFO] +- org.springframework:spring-context-support:jar:4.3.12.RELEASE:compile [INFO] +- org.springframework:spring-tx:jar:4.3.12.RELEASE:compile [INFO] +- javax.xml.bind:jaxb-api:jar:2.3.0:compile [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:compile [INFO] +- javax.inject:javax.inject:jar:1:compile [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile [INFO] |  \- commons-logging:commons-logging:jar:1.1.1:compile [INFO] +- commons-lang:commons-lang:jar:2.6:compile [INFO] +- junit:junit:jar:4.11:test [INFO] +- org.jdom:jdom:jar:2.0.2:compile [INFO] +- commons-io:commons-io:jar:2.4:compile [INFO] +- xalan:xalan:jar:2.7.2:compile [INFO] |  \- xalan:serializer:jar:2.7.2:compile [INFO] |     \- xml-apis:xml-apis:jar:1.4.01:compile [INFO] +- org.apache.derby:derby:jar:10.11.1.1:compile [INFO] +- org.postgresql:postgresql:jar:42.1.1:compile [INFO] +- com.zaxxer:HikariCP:jar:2.6.3:compile [INFO] |  \- org.slf4j:slf4j-api:jar:1.7.25:compile [INFO] +- jaxen:jaxen:jar:1.1.6:compile [INFO] +- ru.testproject:test-conf:jar:2.4.41-SNAPSHOT:compile [INFO] |  +- log4j:log4j:jar:1.2.16:compile [INFO] |  +- org.slf4j:slf4j-log4j12:jar:1.7.25:compile [INFO] |  \- ru.testproject:test-util:jar:2.4.41-SNAPSHOT:compile [INFO] +- com.github.spullara.mustache.java:compiler:jar:0.9.0:compile [INFO] +- org.quartz-scheduler:quartz:jar:2.2.1:compile [INFO] |  \- c3p0:c3p0:jar:0.9.1.1:compile [INFO] +- javax.json:javax.json-api:jar:1.0:compile [INFO] +- org.apache.santuario:xmlsec:jar:2.0.6:compile [INFO] |  +- org.codehaus.woodstox:woodstox-core-asl:jar:4.4.1:compile [INFO] |  |  +- javax.xml.stream:stax-api:jar:1.0-2:compile [INFO] |  |  \- org.codehaus.woodstox:stax2-api:jar:3.1.4:compile [INFO] |  \- commons-codec:commons-codec:jar:1.10:compile [INFO] +- org.eclipse.jetty:jetty-servlets:jar:9.3.5.v20151012:compile [INFO] |  +- org.eclipse.jetty:jetty-continuation:jar:9.4.7.v20170914:compile [INFO] |  +- org.eclipse.jetty:jetty-http:jar:9.4.7.v20170914:compile [INFO] |  +- org.eclipse.jetty:jetty-util:jar:9.4.7.v20170914:compile [INFO] |  \- org.eclipse.jetty:jetty-io:jar:9.4.7.v20170914:compile [INFO] +- com.itextpdf:itextpdf:jar:5.5.12:compile [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.55:compile [INFO] +- org.bouncycastle:bcmail-jdk15on:jar:1.55:compile [INFO] +- org.bouncycastle:bcpkix-jdk15on:jar:1.55:compile [INFO] +- com.google.guava:guava:jar:24.0-jre:compile [INFO] |  +- com.google.code.findbugs:jsr305:jar:1.3.9:compile [INFO] |  +- org.checkerframework:checker-compat-qual:jar:2.0.0:compile [INFO] |  +- com.google.errorprone:error_prone_annotations:jar:2.1.3:compile [INFO] |  +- com.google.j2objc:j2objc-annotations:jar:1.1:compile [INFO] |  \- org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:compile [INFO] +- org.dom4j:dom4j:jar:2.1.0:compile [INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.10:compile [INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.0:compile [INFO] |  \- com.fasterxml.jackson.core:jackson-core:jar:2.8.10:compile [INFO] +- commons-net:commons-net:jar:3.6:compile [INFO] +- org.samba.jcifs:jcifs:jar:1.2.19:compile [INFO] \- org.reflections:reflections:jar:0.9.11:compile [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 15.788 s [INFO] Finished at: 2018-08-22T11:11:41+03:00 [INFO] ------------------------------------------------------------------------ 

Anyway, I have the same issue at application startup:

    Warning: SLF4J: Class path contains multiple SLF4J bindings.     33:08.275 [main] DEBUG org.springframework.boot.logging.ClasspathLoggingApplicationListener - Application failed to start with classpath:  [main] ERROR org.springframework.boot.SpringApplication - Application startup failed java.lang.NoSuchMethodError: org.springframework.util.ObjectUtils.unwrapOptional(Ljava/lang/Object;)Ljava/lang/Object;         at org.springframework.validation.DataBinder.<init>(DataBinder.java:179)         at org.springframework.boot.bind.RelaxedDataBinder.<init>(RelaxedDataBinder.java:83)         org.springframework.boot.context.config.ConfigFileApplicationListener.postProcessEnvironment(ConfigFileApplicationListener.java:197)         org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)         at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:74)         at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)         at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:325)     org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)         at ru.testproject.BootConfiguration.main(BootConfiguration.java:26)     cess finished with exit code 1 

1 Answers

Answers 1

Problem solved: I fixed the dependency issues. Should use,

@Configuration @EnableAutoConfiguration @ComponentScan @Import(Config.class) 

instead @SpringBootApplication annotation.
Now the application is getting booted, but cannot find config file which is another story.

Read More

Tuesday, August 14, 2018

Replacing entire contents of spring-data Page, while maintaining paging info

Leave a Comment

Using spring-data-jpa and working on getting data out of table where there are about a dozen columns which are used in queries to find particular rows, and then a payload column of clob type which contains the actual data that is marshalled into java objects to be returned.

Entity object very roughly would be something like

@Entity @Table(name = "Person")  public class Person {     @Column(name="PERSON_ID", length=45) @Id private String personId;     @Column(name="NAME", length=45) private String name;     @Column(name="ADDRESS", length=45) private String address;     @Column(name="PAYLOAD") @Lob private String payload;      //Bunch of other stuff }  

(Whether this approach is sensible or not is a topic for a different discussion)

The clob column causes performance to suffer on large queries ...

In an attempt to improve things a bit, I've created a separate entity object ... sans payload ...

@Entity @Table(name = "Person")  public class NotQuiteAWholePerson {     @Column(name="PERSON_ID", length=45) @Id private String personId;     @Column(name="NAME", length=45) private String name;     @Column(name="ADDRESS", length=45) private String address;      //Bunch of other stuff }  

This gets me a page of NotQuiteAPerson ... I then query for the page of full person objects via the personIds.

The hope is that in not using the payload in the original query, which could filtering data over a good bit of the backing table, I only concern myself with the payload when I'm retrieving the current page of objects to be viewed ... a much smaller chunk.

So I'm at the point where I want to map the contents of the original returned Page of NotQuiteAWholePerson to my List of Person, while keeping all the Paging info intact, the map method however only takes a Converter which will iterate over the NotQuiteAWholePerson objects ... which doesn't quite fit what I'm trying to do.

Is there a sensible way to achieve this ?

1 Answers

Answers 1

You can avoid the problem entirely with Spring Data JPA features.

The most sensible way would be to use Spring Data JPA projections, which have good extensive documentation.

For example, you would first need to ensure lazy fetching for your attribute, which you can achieve with an annotation on the attribute itself.

i.e. :

@Basic(fetch = FetchType.LAZY)  @Column(name="PAYLOAD") @Lob private String payload; 

or through Fetch/Load Graphs, which are neatly supported at repository-level.

You need to define this one way or another, because, as taken verbatim from the docs :

The query execution engine creates proxy instances of that interface at runtime for each element returned and forwards calls to the exposed methods to the target object.

You can then define a projection like so :

interface NotQuiteAWholePerson {     String getPersonId();     String getName();     String getAddress();      //Bunch of other stuff } 

And add a query method to your repository :

interface PersonRepository extends Repository<Person, String> {      Page<NotQuiteAWholePerson> findAll(Pageable pageable);     // or its dynamic equivalent     <T> Page<T> findAll(Pageable pageable, Class<T>); } 

Given the same pageable, a page of projections would refer back to the same entities in the same session.

If you cannot use projections for whatever reason (namely if you're using JPA < 2.1 or a version of Spring Data JPA before projections), you could define an explicit JPQL query with the columns and relationships you want, or keep the 2-entity setup. You could then map Persons and NotQuiteAWholePersons to a PersonDTO class, either manually or (preferably) using your object mapping framework of choice.

NB. : There are a variety of ways to use and setup lazy/eager relations. This covers more in detail.

Read More