是否可以同时使用JDBC模板和JDBCMySQL?

Is it possible to use JDBC Template and JDBC MySQL at the same time?

我目前正在构建一个简单的基于 CRUD 的应用程序,我已经完成了基本的工作,比如创建一个带有标题、日期、描述等的表单,编辑或删除 post 等。所以现在我也在尝试添加图片上传功能。 (我使用 Windows 10 作为我的 OS)

目前正在学习以下教程URL

https://www.callicoder.com/spring-boot-file-upload-download-jpa-hibernate-mysql-database-example/

,当我看一看

配置数据库和多部分文件属性

部分,它解释了配置数据库和多部分文件属性需要哪些语句,但是当我添加教程页面的示例时,它导致了冲突。

下面是我的 application.properties 文件的样子。

“## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties”行之后是我从教程中复制粘贴的部分,上面是添加教程之前的原始代码)

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.h2.console.enabled=true

## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= root
spring.datasource.password= callicoder

## Hibernate Properties

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update

## Hibernate Logging
logging.level.org.hibernate.SQL= DEBUG

## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB

引起冲突的部分如下。

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa

spring.datasource.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= root

我假设冲突的原因是我试图同时使用JDBC H2数据库和JDBC MySQL。起初我以为像下面这样注释掉我原来的配置就可以解决问题,

#spring.datasource.url=jdbc:h2:mem:test
#spring.datasource.driverClassName=org.h2.Driver
#spring.datasource.username=sa
#spring.h2.console.enabled=true

,但在此之后我无法 运行 程序可能是因为有一部分我使用 JDBC Templace,如下所示。

[ReportDaoImpl.java]

package com.example.demo.repository;

import java.sql.Timestamp;

@Repository
public class ReportDaoImpl implements ReportDao {
    
    private final JdbcTemplate jdbcTemplate; 
    
    @Autowired
    public ReportDaoImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate; 
    }

    @Override
    public List<Report> findAll() {
        String sql = "SELECT report_id, title, threat_level, report_date, description, img_path, "
                            + "user.user_id, user_name FROM report "
                            + "INNER JOIN user ON report.user_id = user.user_id";
        
        List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql); 
……

我最大的问题是,如何在不引起配置冲突的情况下将教程的“上传图像”功能集成到我的基本 CRUD 应用程序中?

我是否应该放弃使用 JDBC H2 数据库和 JDBC 模板并使用其他与我从中获取的 JDBC MySQL 部分兼容的东西教程?换句话说,为了集成教程的图片上传功能,我是否应该从根本上重构我在 ReportDaoImpl.java 文件(甚至可能是其他文件?)中的代码,或者是否有一种简单的方法来解决配置冲突?

您不能在 application.properties 中多次定义同一个键,一个将覆盖另一个。这意味着如果您需要使用多个数据源(对于 MySQL 和 H2),您不能依赖 application.properties 中的 spring.datasource.xxx。相反,您自己明确定义两个 DataSource。例子见官方docs

此外,JdbcTemplate 仅在以下情况下配置:

  • 只定义了一个DataSource
  • 如果定义了多个DataSource,但只有一个DataSource被标记为@Primary,它将只为这个@Primary数据源配置。

所以这意味着在你定义多个 DataSource 之后,你必须将 H2 标记为 @Primary 这样 JdbcTemplate 将自动配置它只是为了确保你现有的 JDBCTempalte 相关代码仍与 H2 交互但不 MySQL.

顺便说一句,对于一个简单的 CRUD 应用程序,使用多个数据库没有任何优势。如果您想要一个涵盖来自多个数据库的数据的事务,您将遇到问题。这么简单的app我建议你只选一个。

(另请参阅我的相关 了解更多详情)

如果您出于绝对必要的原因打算使用它们,则不必放弃任何数据库。 Spring DataSourceAutoConfiguration 不能区分 属性 文件中的两个配置,因为 属性 文件是一个键值对映射。因此它会覆盖配置。

解决此问题的最简单方法是:

  1. 为不同的数据源创建单独的密钥,如下所示:
## Your Primary Data Source
spring.datasource-primary.url=jdbc:h2:mem:test
spring.datasource-primary.driverClassName=org.h2.Driver
spring.datasource-primary.username=sa
spring.h2.console.enabled=true

## Your Secondary Data Source
spring.datasource-secondary.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource-secondary.username= root
spring.datasource-secondary.password= callicoder
  1. 如下添加 DataSourceConfig
package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

/**
 * Configures the Spring-managed resources for Common Services/Utils.
 */
@Configuration
public class DataSourceConfig {

    @Autowired
    Environment env;

    /**
     * Primary DataSource (Meaning the one that is your parent transaction manager)
     */
    @Bean
    @Primary
    public DataSource h2DataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.datasource-primary.driverClassName"));
        dataSource.setUrl(env.getProperty("spring.datasource-primary.url"));
        dataSource.setUsername(env.getProperty("spring.datasource-primary.username"));
        dataSource.setPassword(env.getProperty("spring.datasource-primary.password"));
        return dataSource;
    }

    /**
     * @usage Autowire this in your JPA Repositories using
     *      @Autowired
     *      JdbcTemplate h2JdbcTemplate;
     */
    @Bean
    public JdbcTemplate h2JdbcTemplate() {
        return new JdbcTemplate(h2DataSource());
    }

    /**
     * Secondary DataSource (Meaning the one that can cause the parent transaction to roll-back on exception)
     */
    @Bean
    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.datasource-secondary.driverClassName"));
        dataSource.setUrl(env.getProperty("spring.datasource-secondary.url"));
        dataSource.setUsername(env.getProperty("spring.datasource-primary.username"));
        dataSource.setPassword(env.getProperty("spring.datasource-secondary.password"));
        return dataSource;
    }

    /**
     * @usage Autowire this in your JPA Repositories using
     *      @Autowired
     *      JdbcTemplate mysqlJdbcTemplate;
     */
    @Bean
    public JdbcTemplate mysqlJdbcTemplate() {
        return new JdbcTemplate(mysqlDataSource());
    }
}

  1. 在您的存储库中正确使用 JdbcTemplate 类
package com.example.demo.repository;

import java.sql.Timestamp;

@Repository
public class ReportDaoImpl implements ReportDao {

    //Note the JdbcTemplate variable name here
    private final JdbcTemplate myslJdbcTemplate; 

    @Autowired
    //Note the JdbcTemplate variable name here
    public ReportDaoImpl(JdbcTemplate myslJdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate; 
    }

    @Override
    public List<Report> findAll() {
        String sql = "SELECT report_id, title, threat_level, report_date, description, img_path, "
                            + "user.user_id, user_name FROM report "
                            + "INNER JOIN user ON report.user_id = user.user_id";

        List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql); 
……

您需要为所有相应的存储库更新相应的 JdbcTemplate 类。

干杯,编码愉快!