How to use JPA Repositories without Spring Boot
Asked Answered
T

2

6

I have an existing spring 4 project (mvc, jdbc etc) and I tried to port it to spring boot and I can't. (many dependencies troubles, no one can't explain how I can do that). But now I just want to use Spring Data JPA in existing project. That is a main pom dependencies:

<properties>
        <jetty.version>9.3.5.v20151012</jetty.version>
        <spring.version>4.3.12.RELEASE</spring.version>
        <spring.boot.version>1.5.8.RELEASE</spring.boot.version>
        <spring.security.version>4.2.3.RELEASE</spring.security.version>
        <quartz.version>2.2.1</quartz.version>
        <slf4j.version>1.7.5</slf4j.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <hibernate-version>5.3.3.Final</hibernate-version>
    </properties>

<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>
    <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.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>

I have existing configuration, that I completed with entityManagerFactory Bean (that used in all examples). DBConfig:

@Configuration
@ComponentScan("ru.testproject.*")
@EnableTransactionManagement
@EnableJpaRepositories("ru.testproject.*")
public class DatabaseConfig {

    @Value("${postgresql.address}")
    String address;
    @Value("${postgresql.database}")
    String database;
    @Value("${postgresql.user}")
    String user;
    @Value("${postgresql.password}")
    String password;
    @Value("${db.type}")
    String dbType;

    @Bean
    public DataSource dataSource() {

        if (DB_TYPE_POSTGRESQL.equalsIgnoreCase(dbType)) {
            return postresqlDataSource();
        } else {
            return derbyDataSource();
        }

    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);

        return dataSourceTransactionManager;
    }

    private HikariDataSource derbyDataSource() {

        try {
            String appHome = System.getProperty(Property.APP_HOME);

            Path path = Paths.get(appHome, DATABASE_NAME);
            String databaseName = path.toString();

            databaseName = StringUtils.replaceChars(databaseName, '\\', '/');

            Class.forName(DB.DB_DRIVER_DERBY).newInstance();

            EmbeddedDataSource embeddedDataSource = new EmbeddedDataSource();
            String dbName = databaseName;

            embeddedDataSource.setDatabaseName(dbName);
            embeddedDataSource.setCreateDatabase("create");

            embeddedDataSource.setUser("application");
            embeddedDataSource.setPassword("");

            HikariConfig config = new HikariConfig();
            config.setDataSource(embeddedDataSource);
            config.setMaximumPoolSize(10);
            config.setAutoCommit(true);

            HikariDataSource hikariDataSource = new HikariDataSource(config);

            return hikariDataSource;
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new IllegalStateException("error connection Derby");
        }

    }

    private HikariDataSource postresqlDataSource() {

        if (StringUtils.isBlank(address)
                || StringUtils.isBlank(database)
                || StringUtils.isBlank(user)
                || StringUtils.isBlank(password)) {
            throw new IllegalStateException("error params Postgresql");
        }

        HikariConfig config = new HikariConfig();
        String jdbcUrl = MessageFormat.format(POSTGRESQL, address, database);
        config.setJdbcUrl(jdbcUrl);
        config.setUsername(user);
        config.setPassword(password);
        config.setMaximumPoolSize(10);
        config.setAutoCommit(false);

        return new HikariDataSource(config);
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan("ru.testproject.hibernate.*");
        factory.setDataSource(dataSource());
        factory.afterPropertiesSet();
        factory.setPersistenceUnitName("test");
        return factory;
    }

}

Derby database used for tests. But main database type is PostgreSQL with HikariCP. I'm already add HQL support in project and it works ok. But when I try to add JAP repositories support, I have many problems. I have entity:

@Entity
@Table(name = "users", schema = "public", catalog = "test")
public class Users {
    private int id;
    private String username;
    private String password;
    private String email;

    @Id
    @Column(name = "id", nullable = false)
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "username", nullable = false, length = 64)
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Basic
    @Column(name = "password", nullable = false, length = 64)
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Basic
    @Column(name = "email", nullable = false, length = 128)
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Users that = (Users) o;
        return id == that.id &&
                Objects.equals(username, that.username) &&
                Objects.equals(password, that.password) &&
                Objects.equals(email, that.email);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, password, email);
    }
}

And Repository:

@Repository
public interface UsersRepository extends CrudRepository<Users, Long> {

    @Query("select b from Users b where b.username = :username")
    Users findByName(@Param("username") String username);
}

it's just a test repository extends CrudRepository. For using that repository I've created service with implementation:

@Service
public interface UserService {

    void delete(long id);
    Users getByName(String name);
    Optional<Users> getById(Long id);
    Users editUsers(Users user);
}

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UsersRepository usersRepository;

//    @PersistenceContext(unitName = "test")
//    private EntityManager em;

    @Override
    public void delete(long id) {
        usersRepository.deleteById(id);
    }

    @Override
    public Optional<Users> getById(Long id) {
        return usersRepository.findById(id);
    }

    @Override
    public Users getByName(String name) {
        return usersRepository.findByName(name);
    }

    @Override
    public Users editUsers(Users user) {
        return usersRepository.save(user);
    }
}

And I'm using repository like that:

@Autowired
    private UserService service;
...
Users entry = service.getById(1L).get();

that is all. I don't use Spring Boot (as said above) and start as application failed with error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in ru.testproject.config.DBConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'entityManagerFactory' threw exception; nested exception is java.lang.NoSuchFieldError: parallelCapableClassLoaderAvailable
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:587)

If I comment @EnableJpaRepositories annotation, I have the following error:

Unsatisfied dependency expressed through field 'userServiceImpl'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userServiceImpl': Unsatisfied dependency expressed through field 'usersRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ru.testproject.db.hibernate.repository.UsersRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

In second scenario repository is null, Certainly - JPA repo's disabled.. Please, help! What I'm doing wrong and how can I make it works? I can't resolve my problem with google and Internet lessions. Maybe because I don't understand what is the root problem.

Totalitarianism answered 20/8, 2018 at 13:24 Comment(4)
#23974749 In that thread, last comment describes the same issue with EntityManagerFactory.Totalitarianism
Read the manual docs.spring.io/spring-data/jpa/docs/2.0.9.RELEASE/reference/… and have a look at the examples github.com/spring-projects/spring-data-examples/tree/master/jpaWooster
I've already check many examples and following manuals describes spring boot configurations. It's not exactly what I'm looking for.Totalitarianism
Could you try with replacing @EnableJpaRepositories("ru.testproject.*") into @EnableJpaRepositories("ru.testproject.db.hibernate.repository")Dannielledannon
T
3

Thx to all for Help! I've solved my issue with the following changes: 1) First of all - I've update spring-context dependency with newer version:

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.4.RELEASE</version>
    </dependency>

it solves java.lang.NoClassDefFoundError: org/springframework/context/index/CandidateComponentsIndexLoader error. CandidateComponentsIndexLoader appears after spring-context 5.0.0.

2) I've add the

@EnableJpaRepositories(basePackages = {
        "ru.testproject.hibernate"
})
@PropertySource("classpath:testproject.properties")
@ComponentScan("ru.testproject.hibernate")

annotations. Previous changes didn't affect to any positive changes. With 1) it lead to entityManagerFactory error, that I can solve with:

3) Set up packgeToScan for entityManagerFactory with

entityManagerFactoryBean.setPackagesToScan("ru.testproject.hibernate.entity");

with path for entities (Users in that case).

That is all. Now repositories works perfectly. Thank you all!!!

ps: may be, now I can start with spring boot)

Totalitarianism answered 21/8, 2018 at 10:27 Comment(0)
V
0

It seems you are not adding the test package ru.testproject.db.hibernate.repository in component scan.

Vacua answered 20/8, 2018 at 15:33 Comment(3)
@ComponentScan("ok.testproject.hibernate.repository"), but it doesn't workTotalitarianism
but error its showing the "ru.testproject.db.hibernate.repository" package. can you confirm you are using correct package name ?Vacua
Yes, i've just rename the package. But you are right: some troubles was caused with wrong component pathesTotalitarianism

© 2022 - 2024 — McMap. All rights reserved.