0

I've been working on a Spring Project and encountered an unusual error. I have an Entity class defined as

import jakarta.persistence.*;
import lombok.Data;
// other imports

@Table(
        name = "brand"
)
@Entity
@Data
public class Brand {
    @Id
    @Column(
            name = "id"
    )
    @GeneratedValue(
            strategy = GenerationType.UUID
    )
    String id;
    @Enumerated(EnumType.STRING)
    ResourceType resourceType;
    String name;
    String defaultLanguage;
    @Enumerated(EnumType.STRING)
    ReviewStage reviewStage;

}

public enum ResourceType {
    PUBLIC,
    MEDIA_OUTLET, // Other Values
}

public enum ReviewStage {
    PRE_SUBMIT,
    SUBMITTED,
    REJECTED,
    ACCEPTED,
    DECLINED,
    CONFIRMED,
    CONFIRM_APPEALED;
}

Here is my Repository:

public interface BrandRepo extends JpaRepository<Brand, String> {

    @Query(value = "select * from brand i where i.name like CONCAT('%', ?1, '%') " +
            "and i.reviewStage = :#{#stage?.name()}", nativeQuery = true)
    //@Query("select i from Brand i where i.name like %:name% and i.reviewStage = :stage")
    List<Brand> getLikeName(String name, ReviewStage stage, Pageable pageable);
}

In my database, I have a few objects with both the resourceType and reviewStage columns set to some value. But when I execute the method, the object returns, but the fields are null. When I use the non-native query version in the JPA Repository, the object does NOT return.

Right now, I'm using Spring Boot version 3.5.0 - A recent update, I may have been using a 2.x version when I first persisted these objects to the database (mentioning this in case the issue is a bug in a recent version of Spring Boot).

Other Gradle Dependencies:

    implementation 'org.hibernate:hibernate-core:6.1.6.Final'
    implementation 'org.hibernate:hibernate-entitymanager:5.6.14.Final'
    implementation 'jakarta.validation:jakarta.validation-api:3.0.2'
    implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springbootVersion"  // Note, this is 3.5.0
    implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'

    implementation 'com.mysql:mysql-connector-j:9.3.0'
    implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:+'

I have tried replacing my Hibernate dependencies with

implementation 'org.hibernate.orm:hibernate-core:7.0.3.Final'

This led me to Google (Bing, actually) java.lang.ClassNotFoundException: org.hibernate.query.BindableType so I reverted back.

Edit: Made sure Entity name was consistent throughout post.

Edit 2: I have not solved the problem yet. However, I have done a little digging to see if I could narrow down the problem. I decided to Create a Converter class and I've updated my entity class as follows:

@Table(
        name = "brand"
)
@Entity
@Data
public class Brand {
    @Id
    @Column(
            name = "id"
    )
    @GeneratedValue(
            strategy = GenerationType.UUID
    )
    String id;
    @Enumerated(EnumType.STRING)
    ResourceType resourceType;
    String name;
    String defaultLanguage;
    @Convert(converter = ReviewStageConverter.class)
    ReviewStage reviewStage;
    String brandId;


    public void setReviewStage(ReviewStage reviewStage1){
        this.reviewStage = reviewStage1;
    }

    public void setReviewStage(String reviewStage1) {
        this.reviewStage = ReviewStage.valueOf(reviewStage1.trim());
    }
}

Here are the results of running the Converter: Debug of Converter class showing that the input string was null

I've also enabled Hibernate SQL logging and here is the statement used: Log of the Hibernate SQL statement used

Lastly, because I considered the possibility that the issue was with either the MySQL driver I was using or with JDBC (or the enum values just weren't there in my DB), I created a JDBC-stub application and here are the results (the real entity class is a bit larger than what was shown before): Results of JDBC-stub application showing that enum values are appearing

Edit 3: Earlier today, I created a smaller project that used both Spring JPA and JDBC to retrieve data. My logic works correctly in the smaller project. I feel compelled to provide additional context that I previously did not consider relevant.

The larger project is connected to multiple databases (in theory), something that is not true of the smaller project. It uses a library that had two JPA DataSource's and a configuration file for its own purposes.

Here is the configuration file for it's own DataSource:

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 org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "resourcesEntityManagerFactory",
        transactionManagerRef = "resourcesTransactionManager",
        basePackages = {"com.tc.resources.repos"})

public class ResourceRepoConfig {

    @Bean(name = "resourcesDataSourceProperties")
    @ConfigurationProperties("spring.datasource")
    public DataSourceProperties primaryDataSourceProperties()
    {
        return new DataSourceProperties();
    }


    @Bean(name = "resourcesDataSource")
    @ConfigurationProperties("spring.datasource.configuration")
    public DataSource primaryDataSource(@Qualifier("resourcesDataSourceProperties") DataSourceProperties primaryDataSourceProperties) {
        DataSource ds = primaryDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        return ds;
    }


    @Bean(name = "resourcesEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
            EntityManagerFactoryBuilder primaryEntityManagerFactoryBuilder,
            @Qualifier("resourcesDataSource") DataSource primaryDataSource) {

        Map<String, String> primaryJpaProperties = new HashMap<>();
        primaryJpaProperties.put("hibernate.hbm2ddl.auto", "update");
        primaryJpaProperties.put("hibernate.enable_lazy_load_no_trans", "true");

        LocalContainerEntityManagerFactoryBean ret = primaryEntityManagerFactoryBuilder
                .dataSource(primaryDataSource)
                .packages( "com.tc.resources.models")
                .persistenceUnit("resourcesDataSource")
                .properties(primaryJpaProperties)
                .build();

        return ret;
    }


    @Bean(name = "resourcesTransactionManager")
    public PlatformTransactionManager primaryTransactionManager(
            @Qualifier("resourcesEntityManagerFactory") EntityManagerFactory primaryEntityManagerFactory) {

        return new JpaTransactionManager(primaryEntityManagerFactory);
    }
}
2
  • The (:#{...}) expressions only work in JPQL queries, i.e. nativeQuery = false (or defaults to JPQL if not written at all). Commented Jun 25 at 7:01
  • If you are using native query (nativeQuery = true), then it should look like this: @Query(value = "SELECT * FROM brand i WHERE i.name LIKE %?1% AND i.review_stage = ?2", nativeQuery = true) List<Brand> getLikeName(String name, String reviewStageName, Pageable pageable); Commented Jun 25 at 7:02

4 Answers 4

1

First cleanup your dependencies

implementation "org.springframework.boot:spring-boot-starter-validation" 
implementation "org.springframework.boot:spring-boot-starter-data-jpa"

implementation 'com.mysql:mysql-connector-j:9.3.0'
  1. The spring-boot-starter-data-jpa contains already the necessary dependencies for Hibernate to be used with JPA.
  2. Use spring-boot-starter-validation to add validation to your application. Don't add individual dependencies as that well lead to issues with upgrading and compatibility.

Write a proper query method instead of custom query With Spring Data JPA you can write simple query methods without having to resort to write a query yourself.

List<Brand> findByNameContainingAndReviewStage(String name, ReviewStage reviewStage, Pageable pageable);

This is all you need, no need for a query. If you wanted to write a query to start with I would suggest writing a JPQL not a SQL query as that will lead to mapping issues (as you have seen here).

Sign up to request clarification or add additional context in comments.

4 Comments

I'm just seeing an empty list. Before, I was seeing something in the list.
Could you try with findByNameContainingAndReviewStage although Contains should work (or maybe only on lists).
I'm afraid I'm still just seeing an empty list
Then the query returns nothing. Check if the query is what you expect (you can enable SQL printing in Hibernate). Is the query you posted the actual query or was it also doing other thinbgs like ignoring case etc?
0

Maybe you must be write inside JpaRepository Brand entity not BrandInfo. Please try this and check again

public interface BrandRepo extends JpaRepository<Brand, String>

also

@Query("SELECT i FROM Brand i WHERE i.name LIKE %:name% AND i.reviewStage = :stage")

List<Brand> getLikeName(@Param("name") String name, @Param("stage") ReviewStage stage, Pageable pageable);

3 Comments

Thats not it. I meant to mask "BrandInfo" as "Brand" when posting my question. Apparently, I forgot one instance.
Yes, it confuses me that you write like this.
I'm just seeing an empty list now.
0

I test it it works fine can you try my build.gradle file

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.3'
    id 'io.spring.dependency-management' version '1.1.7'
}

group = 'org.softylines'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    // Spring Boot starters
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-web'

    // Lombok
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    // PostgreSQL driver
    runtimeOnly 'org.postgresql:postgresql'

    // Test dependencies
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform()
}

Comments

0

Please change your code to this:

@Query("select i from Brand i where i.name like %:name% and i.reviewStage = :stage")
Page<Brand> getLikeName(@Param("name") String name, @Param("stage") ReviewStage stage, Pageable pageable);

or this one if you want your nativeQuery be true:

@Query(value = "select * from brand i where i.name like CONCAT('%', ?1, '%') and i.review_stage = ?2", nativeQuery = true)
List<Brand> getLikeName(String name, String reviewStage, Pageable pageable);

1 Comment

For the first one, I'm seeing an empty list. For the second one (I had to change 'review_stage' to 'reviewStage'), I'm still seeing the enums as null values.

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.