3

Following the solution here, I wanted to add two additional datasources for my application (both SQL Server). My setup would be:

  • Let JHipster have its own database, accessing it with JPA. In development environment use H2, in production SQL Server.
  • Add two SQL Server dataSource that I would use only with JDBC (JdbcTemplate). These are read-only databases and I only need to run queries on them.

So, I generated a new JHipster Monolithic app (6.0.1), with disk-based h2 for dev and SQL Server in production. I changed the DatabaseConfigration to this:

@Configuration
@EnableJpaRepositories(".....repository")
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
@EnableTransactionManagement
public class DatabaseConfiguration {

    private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);

    private final Environment env;

    public DatabaseConfiguration(Environment env) {
        this.env = env;
    }

    /**
     * Open the TCP port for the H2 database, so it is available remotely.
     *
     * @return the H2 database TCP server.
     * @throws SQLException if the server failed to start.
     */
    @Bean(initMethod = "start", destroyMethod = "stop")
    @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
    public Object h2TCPServer() throws SQLException {
        String port = getValidPortForH2();
        log.debug("H2 database is available on port {}", port);
        return H2ConfigurationHelper.createServer(port);
    }

    private String getValidPortForH2() {
        int port = Integer.parseInt(env.getProperty("server.port"));
        if (port < 10000) {
            port = 10000 + port;
        } else {
            if (port < 63536) {
                port = port + 2000;
            } else {
                port = port - 2000;
            }
        }
        return String.valueOf(port);
    }

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource")
    public DataSourceProperties defaultDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.hikari")
    public DataSource defaultDataSource() {
        return defaultDataSourceProperties().initializeDataSourceBuilder()
            //.type(HikariDataSource.class)
            .build();
    }

    @Bean(name = "entityManagerFactory")
    @Primary
    public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
        EntityManagerFactoryBuilder builder) {
        return builder
            .dataSource(defaultDataSource())
            .packages(MyMainApplicationClassApp.class)
            .persistenceUnit("default")
            .build();
    }

    @Bean(name = "transactionManager")
    @Primary
    public JpaTransactionManager db2TransactionManager(@Qualifier("entityManagerFactory") final EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }
}

And I created a new config class, named AdditionalDatabaseConfiguration and first I wanted to just add one of my additional databases:

@Configuration
@EnableTransactionManagement
public class AdditionalDatabaseConfiguration {

    @Bean
    @ConfigurationProperties("sync.datasource")
    public DataSourceProperties SyncDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("sync.datasource.hikari")
    public HikariDataSource SyncDataSource() {
        var properties = SyncDataSourceProperties();
        return properties.initializeDataSourceBuilder()
            .type(HikariDataSource.class)
            .build();
    }
}

I did not change the default spring.datasource configuration yet, but I did add config for the new ds:

sync:
    datasource:
        type: com.zaxxer.hikari.HikariDataSource
        url: jdbc:sqlserver://***:1433;database=***
        username: ****
        password: ***
        testOnBorrow: true
        validationQuery: SELECT 1
        hikari:
            poolName: HikariSync
            auto-commit: false
            connection-test-query: SELECT 1
            maximum-pool-size: 2
            idle-timeout: 10000
            data-source-properties:
                cachePrepStmts: true
                prepStmtCacheSize: 250
                prepStmtCacheSqlLimit: 2048
                useServerPrepStmts: true

The app compiles and starts without error or warning and the main page of the application loads too. However If I try to login, I get an exception:

https://pastebin.com/raw/HWSEQGyq

org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [call next value for sequenceGenerator]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:279)
        at org.sprin.....
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
        ... 130 common frames omitted
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Sequence "SEQUENCEGENERATOR" not found; SQL statement:
call next value for sequenceGenerator [90036-199]
        at org.h2.message.DbException.getJdbcSQLException(DbException.java:573)
        at org.h2.message.DbException.getJdbcSQLException(DbException.java:427)
        at org.h2.message.DbException.get(DbException.java:205)
        at org.h2.message.DbException.get(DbException.java:181)
        at org.h2.command.Parser.readSequence(Parser.java:7196)
        at org.h2.command.Parser.readTermWithIdentifier(Parser.java:4192)
        at org.h2.command.Parser.readTerm(Parser.java:3972)
        at org.h2.command.Parser.readFactor(Parser.java:3090)
        at org.h2.command.Parser.readSum(Parser.java:3077)
        at org.h2.command.Parser.readConcat(Parser.java:3047)
        at org.h2.command.Parser.readCondition(Parser.java:2900)
        at org.h2.command.Parser.readAnd(Parser.java:2872)
        at org.h2.command.Parser.readExpression(Parser.java:2864)
        at org.h2.command.Parser.parseCall(Parser.java:6043)
        at org.h2.command.Parser.parsePrepared(Parser.java:848)
        at org.h2.command.Parser.parse(Parser.java:788)
        at org.h2.command.Parser.parse(Parser.java:760)
        at org.h2.command.Parser.prepareCommand(Parser.java:683)
        at org.h2.engine.Session.prepareLocal(Session.java:627)
        at org.h2.engine.Session.prepareCommand(Session.java:565)
        at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1292)
        at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:77)
        at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:349)
        at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:311)
        at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java)
        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$1.doPrepare(StatementPreparerImpl.java:87)
        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:172)
        ... 165 common frames omitted

Probably my @Primary datasource configuration/code isn't correct. Could anyone please tell me what to add?

Edit.: If I remove my datasource configs, I can login, so it is the cause of the error.

Edit.: The main problem is Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Sequence "SEQUENCEGENERATOR" not found; SQL statement: call next value for sequenceGenerator [90036-199]

Edit2.: Setting the dialect for the default datasource did not solve the issue (using yml):

  jpa:
    database-platform: io.github.jhipster.domain.util.FixedH2Dialect <-- maybe this is the issue?
    database: H2
    show-sql: true
    properties:
      hibernate.id.new_generator_mappings: true
      hibernate.connection.provider_disables_autocommit: true
      hibernate.cache.use_second_level_cache: true
      hibernate.cache.use_query_cache: false
      hibernate.generate_statistics: false
      hibernate.dialect: org.hibernate.dialect.H2Dialect <-- this

Edit3: I generated a new project without using H2 on dev profile (directly SQL Server), adding the same multi datasource config and the problem persists. I just get a com.microsoft.sqlserver.jdbc.SQLServerException: Invalid object name 'sequenceGenerator'. exception.

Edit4.: My MSSQL problem can be solved by changing the generator name to sequence_generator in domain.User and domain.PersistentAuditEvent files. H2 specific problem?

tasukete kudasai

szab.kel
  • 2,356
  • 5
  • 40
  • 74
  • Don't know where you see the exception in your title, but `org.h2.jdbc.JdbcSQLSyntaxErrorException: Sequence "SEQUENCEGENERATOR" not found` would be the actual exception here – XtremeBaumer Jun 07 '19 at 10:10
  • @XtremeBaumer The first line? Caused by a `SQLGrammarException` where the nested exception is a `JdbcSQLSyntaxErrorException` – szab.kel Jun 07 '19 at 11:10
  • `InvalidDataAccessResourceUsageException` is in the end caused by the `JdbcSQLSyntaxErrorException`. So that is the exception you should actually look at. The question title is including the wrong exception – XtremeBaumer Jun 07 '19 at 11:16
  • Does your fourth edit mean that the generated app now successfully works with a MSSQL dB? – Leif Jones Nov 03 '19 at 23:57
  • 1
    @LeifSegen Yes, I could not use H2 for dev – szab.kel Nov 05 '19 at 06:45

1 Answers1

0

In my case jhipster generated different names for the sequence (I've selected oracle db for production) "hibernate_sequence" in 00000000000000_initial_schema.xml

and "sequenceGenerator" in domain entities

@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") @SequenceGenerator(name = "sequenceGenerator") private Long id;

The problem has been resolved by setting the same name in both places.