You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在JSF中复用JDBC数据源实现多Bean数据库访问的问题

How to Reuse a Single JDBC DataSource Across Multiple JSF Beans

Alright, let's break down why you're hitting this issue first: Each @DataSourceDefinition annotation on a bean class creates a separate, independent data source instance—even if all the configuration parameters are identical. That means your UserRepo and Customer bean are using two completely separate connection pools under the hood, which can lead to unexpected behavior like inconsistent query results, transaction context mismatches, or connection pool state issues.

The fix is to define your data source once at the application level, then inject that single shared instance into all your beans. Here are three robust approaches to do this:


1. Global @DataSourceDefinition on an Application-Scoped Class

Instead of attaching @DataSourceDefinition to each repository/bean, place it on a dedicated application-scoped configuration class. This ensures only one data source instance is created for the entire app.

Step 1: Create a Global Data Source Config

import javax.annotation.sql.DataSourceDefinition;
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
@DataSourceDefinition(
    name = "java:app/jdbc/SharedDB", // Unique JNDI name
    className = "com.mysql.cj.jdbc.MysqlDataSource", // Use your DB driver class
    url = "jdbc:mysql://localhost:3306/your_db",
    user = "db_user",
    password = "db_password",
    // Optional: Configure connection pool settings
    maxPoolSize = 10,
    minPoolSize = 2
)
public class SharedDataSourceConfig {
    // No additional code needed—this class just hosts the data source definition
}

Step 2: Inject the Shared Data Source into Your Beans

Now inject the same data source into both UserRepo and Customer bean using its JNDI name:

import javax.annotation.Resource;
import javax.enterprise.context.RequestScoped;
import javax.sql.DataSource;

@RequestScoped
public class UserRepo {
    @Resource(lookup = "java:app/jdbc/SharedDB")
    private DataSource dataSource;

    // Your query methods here (use this data source for connections)
}
@RequestScoped
public class CustomerBean {
    @Resource(lookup = "java:app/jdbc/SharedDB")
    private DataSource dataSource;

    // Your query methods here—same shared data source as UserRepo
}

2. CDI Producer Method for Flexible Configuration

If you need more control (like reading credentials from a config file, or programmatically setting connection pool parameters), use a CDI producer to create and expose the shared data source.

Step 1: Create a Data Source Producer

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.sql.DataSource;
import com.mysql.cj.jdbc.MysqlDataSource;

@ApplicationScoped
public class DataSourceProducer {
    @Produces
    @ApplicationScoped
    public DataSource createSharedDataSource() {
        MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/your_db");
        dataSource.setUser("db_user");
        dataSource.setPassword("db_password");
        // Configure connection pool settings as needed
        dataSource.setMaxPoolSize(10);
        dataSource.setConnectionTimeout(5000);
        return dataSource;
    }
}

Step 2: Inject the Produced Data Source

Now you can inject the data source directly with @Inject (no JNDI lookup needed):

import javax.inject.Inject;
import javax.enterprise.context.RequestScoped;
import javax.sql.DataSource;

@RequestScoped
public class UserRepo {
    @Inject
    private DataSource dataSource;

    // Query logic here
}
@RequestScoped
public class CustomerBean {
    @Inject
    private DataSource dataSource;

    // Query logic here—same shared data source
}

3. Container-Managed Data Source (For Servlet Containers like Tomcat)

If you're using a servlet container (e.g., Tomcat, Jetty), you can configure the data source directly in the container's configuration files. This is ideal for production environments where you want to separate DB config from your application code.

Step 1: Configure Data Source in context.xml

Add this to your Tomcat conf/context.xml (or a project-specific META-INF/context.xml):

<Resource name="jdbc/SharedDB"
          auth="Container"
          type="javax.sql.DataSource"
          driverClassName="com.mysql.cj.jdbc.Driver"
          url="jdbc:mysql://localhost:3306/your_db"
          username="db_user"
          password="db_password"
          maxTotal="10"
          maxIdle="5"
          maxWaitMillis="5000"/>

Step 2: Reference the Data Source in web.xml

Add a resource reference to your app's web.xml:

<resource-ref>
    <description>Shared Database Connection</description>
    <res-ref-name>jdbc/SharedDB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>

Step 3: Inject the Container-Managed Data Source

Inject it into your beans using the JNDI lookup:

@Resource(lookup = "java:comp/env/jdbc/SharedDB")
private DataSource dataSource;

Why Your Original Approach Failed

When you put @DataSourceDefinition on each bean, the CDI container creates a unique data source instance for each bean class. Even with identical configs, these are separate connection pools—so your Customer bean was using a different pool than UserRepo, which could lead to issues like uncommitted transactions, stale connections, or inconsistent query results depending on your DB state.

By centralizing the data source definition, you ensure all beans use the same connection pool and data source instance, eliminating these inconsistencies while keeping your beans modular (no "god class" required!).

内容的提问来源于stack exchange,提问作者user9782268

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:22:29