2

Trying connect IBMi machine by using Spring Data & JPA, but it's generating the error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [HibernateJpaConfiguration.class]: 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.AbstractMethodError: Receiver class com.ibm.as400.access.AS400JDBCConnectionImpl does not define or inherit an implementation of the resolved method 'abstract boolean isValid(int)' of interface java.sql.Connection.
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.8.jar:5.3.8]
    at spring.security.as400.SpringSecurityDemoApplication2.main(SpringSecurityDemoApplication2.java:10) ~[classes/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'entityManagerFactory' threw exception; nested exception is java.lang.AbstractMethodError: Receiver class com.ibm.as400.access.AS400JDBCConnectionImpl does not define or inherit an implementation of the resolved method 'abstract boolean isValid(int)' of interface java.sql.Connection.
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.8.jar:5.3.8]
Caused by: java.lang.AbstractMethodError: Receiver class com.ibm.as400.access.AS400JDBCConnectionImpl does not define or inherit an implementation of the resolved method 'abstract boolean isValid(int)' of interface java.sql.Connection.
    at com.zaxxer.hikari.pool.PoolBase.checkValidationSupport(PoolBase.java:458) ~[HikariCP-3.4.5.jar:na]

The project is a starter project for learning Spring Data using JPA to connect with IBMi machine, but seems Spring Data doesn't provide the easiest way for this.

My Project classes are:

  1. 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>2.4.7</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>Spring-Security-JT400</groupId>
        <artifactId>Security-JT400</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>Security-JT400</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </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-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            
            
            <dependency>
                <groupId>net.sf.jt400</groupId>
                <artifactId>jt400</artifactId>
                <version>10.6</version>
                <scope>runtime</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
  1. Configuration
     @Configuration
        @EnableWebSecurity
        public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter
    {
        @Autowired
        CustomUserDetailsService customUserDetailsService;
    
        @Bean
        AuthenticationProvider authenticationProvider()
        {
            DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
            provider.setUserDetailsService(customUserDetailsService);
            provider.setPasswordEncoder(new BCryptPasswordEncoder());
    
            return provider;
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception
        {
            http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/home").hasAuthority("USER")
                .antMatchers("/admin").hasAuthority("ADMIN")
                .anyRequest().authenticated()
                .and()
                .httpBasic();
        }
    }
  1. CustomUserDetailsService
    @Service
    public class CustomUserDetailsService implements UserDetailsService
    {
        @Autowired
        private UserRepository userRepository;
        
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
        {
            User user = userRepository.findByUsername(username);
            if (user == null)
            {
                throw new UsernameNotFoundException("User not found.");
            }
    
            return new CustomUserDetails(user);
        }
    }
    @Repository
    public interface UserRepository extends JpaRepository<User, Integer>
    {
        public User findByUsername(String username);
    }
    @RestController
    public class HomeController
    {
        @GetMapping("/home")
        public String home()
        {
            return "This is Home Page.";
        }
    
        @GetMapping("/admin")
        public String admin()
        {
            return "This is Admin Page.";
        }
        
    }
    public class CustomUserDetails implements UserDetails
    {
        private User user;
        
        public CustomUserDetails(User user)
        {
            super();
            this.user = user;
        }
    
        private static final long serialVersionUID = -228521734196009612L;
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities()
        {
            return Collections.singleton(new SimpleGrantedAuthority(user.getRole()));
        }
    
        @Override
        public String getPassword()
        {
            return user.getPassword();
        }
    
        @Override
        public String getUsername()
        {
            return user.getUsername();
        }
    
        @Override
        public boolean isAccountNonExpired()
        {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked()
        {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired()
        {
            return true;
        }
    
        @Override
        public boolean isEnabled()
        {
            return true;
        }
    }
    @Entity
    public class User
    {
        @Id
        private int id;
        private String username;
        private String password;
        private String role;
    }
    @SpringBootApplication
    public class SpringSecurityDemoApplication2 {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringSecurityDemoApplication2.class, args);
        }
    }
  1. application.properties
    spring.datasource.url=jdbc:as400://192.168.xx.xxx/;translate binary=true;prompt=false
    spring.datasource.username=USERNAME
    spring.datasource.password=PASSWORD
    spring.datasource.driver-class-name=com.ibm.as400.access.AS400JDBCDriver
    spring.jpa.database-platform=org.hibernate.dialect.DB2400Dialect

Note: Using same classes and making changes in application.properties, able to connect with MYSQL database.

Kishore_2021
  • 645
  • 12
  • 38
  • The error states that: `Receiver class com.ibm.as400.access.AS400JDBCConnectionImpl does not define or inherit an implementation of the resolved method 'abstract boolean isValid(int)' of interface java.sql.Connection.` But the documentation says that isValid(int) is implemented in jt400 10.6, 10.4 etc, but I don't see this method in source of **com.ibm.as400.access.AS400JDBCConnection** – Kishore_2021 Jun 25 '21 at 04:35
  • Did you resolve it somehow? – Jakub Jakubowski Apr 21 '22 at 17:20

3 Answers3

3

I had the same issue. The problem is that seems that the method abstract boolean isValid(int) is not implemented, or doesn't work correctly (I'm not sure). Basically, it's checking if the connection to DB is healthy. Happily, you can change it to your own. In spring boot just add to application.properties: spring.datasource.hikari.connection-test-query: values 1

For me values 1 worked (I'm using AS400 DB2), but for others DB you may need to use different ones check this answer

I had multiple Datasources and unfortunately adding them to application.properties was not enough, I had to set it in PersistanceConfigurator like this:

@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource as400DataSource() {
    HikariDataSource dataSource = (HikariDataSource) DataSourceBuilder.create().build();
    dataSource.setConnectionTestQuery("values 1");
    return dataSource;
}

Now it's working perfectly fine.

  • As weird as it might seems, this did the trick for me too. However, it seems quite strange that a parameter *apparently* designed for checking on a connection does the ... activation (?) of it – Jueverhard Nov 16 '22 at 16:12
1

Try adding @EnableJpaRepositories annotation below the @SpringBootApplication

0

I'm not (in my particular case) using Spring Data & JPA, just Maven with jt400 version 9.5 and got the same error (isValid not implemented). I upgraded to jt400-jdk8 version 11.1 and that cured the problem.

M. A. Wheadon
  • 101
  • 1
  • 8