0

I'm migrating from Spring Boot 2.7 to Spring Boot 3.3.13. This means that I'm migrating even from Hibernate 5 to Hibernate 6. I'm having an annoying problem when I start my Spring Application about the configuration of my database configuration classes.

This is my one of my database configuration classes that i had on Spring Boot 2.7 (I censored package sensible names):

package ***.datasource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import jakarta.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.HashMap;

/**
 * <p>
 * Data source configuration for Anag Database.
 *
 */

@Configuration   
@ConditionalOnProperty(prefix = "spring.anag.datasource", name = "url")
public class AnagSourceConfiguration {

    @Value("${spring.anag.hibernate.hbm2ddl.auto:validate}")
    private String hibernateHbm2ddlAuto;

    @Value("${hibernate.dialect}")
    private String hibernateDialect;

    @Bean(name = "anagDataSource")
    @ConfigurationProperties("spring.anag.datasource")
    public DataSource anagDataSource() {return DataSourceBuilder.create().build();
    }

    @Bean(name = "anagEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean anagEntityManagerFactory() {
       LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
       em.setDataSource(anagDataSource());
       em.setPackagesToScan("***.entity.anag");
       HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
       em.setJpaVendorAdapter(vendorAdapter);
       final HashMap<String, Object> properties = new HashMap<>();
       properties.put("hibernate.hbm2ddl.auto", hibernateHbm2ddlAuto);
       properties.put("hibernate.dialect", hibernateDialect);
       em.setJpaPropertyMap(properties);
       return em;
    }

    @Bean(name = "anagTransactionManager")
    public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory anagEntityManagerFactory) {
       return new JpaTransactionManager(anagEntityManagerFactory);
    }
}

I manage to update something for the migration: I replaced every javax use with jakarta excluding the DataSource class since it seems a jakarta counterpart doesen't exists. Then, I added the Hibernate dialect on the properties and I replaced "jdbc-url" with "url" on ConditionalProperty name. Did same on yaml file of course.

Then, this is my repositories configuration class:

package ***.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;


@Configuration
@EnableJpaRepositories(
       basePackages = "***.repositories.anag",
       entityManagerFactoryRef = "anagEntityManagerFactory",
       transactionManagerRef = "anagTransactionManager"
)
public class AnagRepositoriesConfig {
}

I dont think I have to change something here.

Now, I'm having this error when I start my application:

Error creating bean with name 'delegatorFilter' defined in file [C:\\Users\\***\\config\\DelegatorFilter.class]: Unsatisfied dependency expressed through constructor parameter 2: Error creating bean with name 'userDelegationService' defined in file [C:\\**\\service\\UserDelegationService.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'userAccountDelegationRepository' defined in **.repositories.anag.UserAccountDelegationRepository defined in @EnableJpaRepositories declared on AnagRepositoriesConfig: Cannot resolve reference to bean 'jpaSharedEM_anagEntityManagerFactory' while setting bean property


Parameter 0 of constructor in **.service.UserDelegationService required a bean named 'anagEntityManagerFactory' that could not be found.\r\n\r\n\r\nAction:\r\n\r\nConsider defining a bean named 'anagEntityManagerFactory' in your configuration

This part is pretty strange: Cannot resolve reference to bean 'jpaSharedEM_anagEntityManagerFactory. It seems a strange concatenation name with jpaSharedEm_ and my bean anagEntityManagerFactory. Why Spring search this bean name? I have to change some name configuration?

I don't think the problem depends from my UserAccountDelegationRepository. This is just the first repository that Spring met when the application starts. I changed the order declaration of repositories on the service and I meet this problem even for other repositories.

Someone met this problem? I see here there are some question about this same problem but I didn't find any relevant answer. I did all the best practices to migrate to Spring Boot 3. There are no more javax on code and I even removed every dependency. Can someone help me?

This is my yaml config about anag datasource:

spring:
  anag:
    datasource:
      url: "jdbc:mysql://xxxxxxxx:3306/anag?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&tinyInt1isBit=false&useSSL=false"
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: xxxxxxxx
      password: xxxxxxxx
    hibernate:
      hbm2ddl:
        auto: validate
10
  • Did you try to use ConditionalOnProperty instead of ConditionalProperty from import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; ? Commented Nov 13 at 2:41
  • Yes sorry. I already wrote ConditionalOnProperty . Just made a mess with copy paste here. Commented Nov 13 at 8:38
  • Probably conflict with configs. Tried to exclude? @SpringBootApplication(exclude = { HibernateJpaAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class }) Commented Nov 13 at 8:42
  • Tried just now but i have same error. I tried a trick naming my bean jpaSharedEM_anagEntityManagerFactory but it didn't work. Commented Nov 13 at 8:54
  • Do you have proper datasource.url property in .properties file? Commented Nov 13 at 9:22

1 Answer 1

0

For information on using non-default Datasource settings, please refer to howto.data-access.configure-two-datasources

This is a proof-of-concept test.

  • application.yaml : remove hibernate: hbm2ddl:
  • AnagSourceConfiguration.java : The main changes are all in this file.
  • AnagRepositoriesConfig.java : The file is not required.

Database - Docker

docker run \
    --name my-mysql-temp \
    -e MYSQL_ROOT_PASSWORD=Passw0rd! \
    -e MYSQL_DATABASE=demodb \
    -e MYSQL_USER=demouser \
    -e MYSQL_PASSWORD=Passw0rd! \
    -p 3306:3306 \
    -d \
    mysql:8.0.43-debian

Project Tree

demo-mysql-jpa_ST
├── pom.xml
├── README.txt
└── src
    └── main
        ├── resources
        │   └── application.yaml <-- Change
        └── java
            └── com
                └── example
                    ├── DemoApplication.java
                    ├── config
                    │   └── AnagSourceConfiguration.java <-- Change
                    ├── entity
                    │   └── anag
                    │       └── Student.java
                    ├── repo
                    │   └── anag
                    │       └── StudentRepository.java
                    ├── service
                    │   └── anag
                    │       └── StudentService.java
                    └── controller
                        └── anag
                            └── StudentController.java

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.7</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo-mysql-jpa</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo-mysql-jpa</name>
    <description>Demo project for Spring Boot</description>
    <properties>
            <maven.compiler.source>17</maven.compiler.source>
                <maven.compiler.target>17</maven.compiler.target>
                <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    <build>
    <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yaml

  • remove hibernate: hbm2ddl:
spring:
  application:
    name: demo-mysql-jpa
  datasource:
    url: jdbc:mysql://localhost:3306/firstdb
    username: firstuser
    password: Passw0rd1
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  anag:
    datasource:
      url: jdbc:mysql://localhost:3306/demodb
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: demouser
      password: Passw0rd!
    jpa:
      hibernate:
        ddl-auto: update
      show-sql: true

DemoApplication.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

AnagSourceConfiguration.java

The main changes are all in this file.

package com.example.config;

import com.zaxxer.hikari.HikariDataSource;
import jakarta.persistence.EntityManagerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

//@Configuration
//@ConditionalOnProperty(prefix = "spring.anag.datasource", name = "url")
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(
        basePackages = "com.example.repo.anag",
        entityManagerFactoryRef = "anagEntityManagerFactory",
        transactionManagerRef = "anagTransactionManager"
)
public class AnagSourceConfiguration {

/*
    @Value("${spring.anag.hibernate.hbm2ddl.auto:validate}")
    private String hibernateHbm2ddlAuto;

    @Value("${hibernate.dialect}")
    private String hibernateDialect;
*/

/*
    @Bean(name = "anagDataSource")
    @ConfigurationProperties("spring.anag.datasource")
    public DataSource anagDataSource() {return DataSourceBuilder.create().build();
    }
*/

    // 1- DataSourceProperties binding YAML
    @Bean
    @ConfigurationProperties("spring.anag.datasource")
    public DataSourceProperties anagDataSourceProperties() {
        return new DataSourceProperties();
    }

    // 2- create HikariDataSource
    @Bean
    @ConfigurationProperties("anag.datasource.configuration")
    public HikariDataSource anagDataSource(
            @Qualifier("anagDataSourceProperties") DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
    }

/*
    @Bean(name = "anagEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean anagEntityManagerFactory() {
       LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
       em.setDataSource(anagDataSource());
       em.setPackagesToScan("com.example.entity.anag");
       HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
       em.setJpaVendoimport com.zaxxer.hikari.HikariDataSource;
       final HashMap<String, Object> properties = new HashMap<>();
       properties.put("hibernate.hbm2ddl.auto", hibernateHbm2ddlAuto);
       properties.put("hibernate.dialect", hibernateDialect);
       em.setJpaPropertyMap(properties);
       return em;
    }
*/
    // 3- EntityManagerFactory binding DataSource + JPA properties
    @Bean(name = "anagEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean anagEntityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("anagDataSource") DataSource anagDataSource) {
        return builder
                .dataSource(anagDataSource)
                .packages("com.example.entity.anag") // entity package
                .persistenceUnit("anag")
                .build();
    }

/*
    @Bean(name = "anagTransactionManager")
    public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory anagEntityManagerFactory) {
       return new JpaTransactionManager(anagEntityManagerFactory);
    }
*/

    // 4- TransactionManager
    @Bean(name = "anagTransactionManager")
    public PlatformTransactionManager anagTransactionManager(
            @Qualifier("anagEntityManagerFactory") EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
}

The AnagRepositoriesConfig.java file is not required.

Student.java

package com.example.entity.anag;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(length = 100, nullable = false)
    private String name;

    @Column(name = "birth_date")
    private LocalDate birthDate;

    @Column(length = 100)
    private String email;

    @Column(length = 2)
    private String grade;

    @Column(name = "enrollment_date")
    private LocalDate enrollmentDate;
}

StudentRepository.java

package com.example.repo.anag;

import com.example.entity.anag.Student;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface StudentRepository extends JpaRepository<Student, Integer> {
    List<Student> findByName(String name);
}

StudentService.java

package com.example.service.anag;

import com.example.entity.anag.Student;
import com.example.repo.anag.StudentRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Transactional(transactionManager = "anagTransactionManager")
public class StudentService {

    private final StudentRepository repo;

    public List<Student> listAll() {
        return repo.findAll();
    }

    public Optional<Student> findById(Integer id) {
        return repo.findById(id);
    }

    public List<Student> findByName(String name) {
        return repo.findByName(name);
    }

    public Student create(Student student) {
        return repo.save(student);
    }

    public Student update(Integer id, Student student) {
        return repo.findById(id)
                .map(s -> {
                    s.setName(student.getName());
                    s.setBirthDate(student.getBirthDate());
                    s.setEmail(student.getEmail());
                    s.setGrade(student.getGrade());
                    s.setEnrollmentDate(student.getEnrollmentDate());
                    return repo.save(s);
                })
                .orElseThrow(() -> new RuntimeException("Student ID not found: " + id));
    }

    public void delete(Integer id) {
        repo.deleteById(id);
    }
}

StudentController.java

package com.example.controller.anag;

import com.example.entity.anag.Student;
import com.example.service.anag.StudentService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/anag/students")
public class StudentController {

    private final StudentService service;

    @GetMapping
    public List<Student> listAll() {
        return service.listAll();
    }

    @GetMapping("/{id}")
    public Student findById(@PathVariable Integer id) {
        return service.findById(id).orElse(null);
    }

    @GetMapping("/name/{name}")
    public List<Student> findByName(@PathVariable String name) {
        return service.findByName(name);
    }

    // CREATE
    @PostMapping
    public Student create(@RequestBody Student student) {
        return service.create(student);
    }

    // UPDATE
    @PutMapping("/{id}")
    public Student update(@PathVariable Integer id, @RequestBody Student student) {
        return service.update(id, student);
    }

    // DELETE
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Integer id) {
        service.delete(id);
    }
}

Build

mvn clean package

Run

java -jar target/app.jar

Test

Create

curl -X POST http://localhost:8080/api/anag/students \
-H "Content-Type: application/json" \
-d '{
  "name": "Alice",
  "birthDate": "2005-05-20",
  "email": "[email protected]",
  "grade": "A",
  "enrollmentDate": "2023-09-01"
}'

List All

curl http://localhost:8080/api/anag/students

Update

curl -X PUT http://localhost:8080/api/anag/students/1 \
-H "Content-Type: application/json" \
-d '{
  "name": "AliceUpdated",
  "birthDate": "2005-05-20",
  "email": "[email protected]",
  "grade": "A",
  "enrollmentDate": "2023-09-01"
}'

Read Id: 1

curl http://localhost:8080/api/anag/students/1

Read Name is AliceUpdated

curl http://localhost:8080/api/anag/students/name/AliceUpdated

Delete Id: 1

curl -X DELETE http://localhost:8080/api/anag/students/1
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.