JPA:如何设置 MySQL 会话变量?

Posted

技术标签:

【中文标题】JPA:如何设置 MySQL 会话变量?【英文标题】:JPA: How to set MySQL session variables? 【发布时间】:2018-06-26 03:05:21 【问题描述】:

我需要为我的应用程序设置一个 mysql 会话变量,以便按预期使用 MariaDB Galera 集群。 SQL 调用是:SET SESSION wsrep_sync_wait = 1。应在应用程序使用数据库时始终设置。我使用 EclipseLink 作为 JPA 提供程序。

我的问题是:实现这一目标的最佳方法是什么?

选项 1:EclipseLink 会话定制器

persistence.xml注册一个会话定制器:

public class SessionCustomizerImpl implements org.eclipse.persistence.config.SessionCustomizer 

    private final static String WSREP_SYNC_WAIT_CHECK_SQL = "SHOW SESSION VARIABLES LIKE 'wsrep_sync_wait'";
    private final static String WSREP_SYNC_WAIT_SET_SQL = "SET SESSION wsrep_sync_wait = 1";

    @Override
    public void customize(Session session) throws Exception 
        Vector result = session.executeSQL(WSREP_SYNC_WAIT_CHECK_SQL);
        if ((result != null) && !result.isEmpty()) 
            session.executeNonSelectingSQL(WSREP_SYNC_WAIT_SET_SQL);
            // Galera connection detected; wsrep_sync_wait set to 1
         else 
            // No Galera connection detected; wsrep_sync_wait not set
        

    

这对我不起作用。从 EntityManager 查询会话变量返回值0

选项 2:EntityManager 工厂

每次创建新的 EntityManager 时,都会执行 SQL。

public class SyncWaitEntityManagerFactory implements Factory<EntityManager> 
    private final EntityManagerFactory emf;

    @Inject
    public SyncWaitEntityManagerFactory(EntityManagerFactory emf) 
        this.emf = emf;
    

    @Override
    public EntityManager provide() 
        final EntityManager em = emf.createEntityManager();
        // set it
        em.getTransaction().begin();
        em.createNativeQuery("SET SESSION wsrep_sync_wait = 1").executeUpdate();
        em.getTransaction().commit();

        return em;
    

    @Override
    public void dispose(EntityManager instance) 
        if (instance.isOpen()) 
            instance.close();
        
    


这行得通,但我不确定它是否矫枉过正。另外,我担心事务的成本,这只是Query#executeUpdate() 需要,而不是实际的SQL 调用。

选项 3:通过 JDBC URL

将变量和值附加到 JDBC URL(详见here):

    String jdbcUrl = "jdbc:mysql://db.example.test:3306/"+ JDBC_DB 
            +"?sessionVariables=wsrep_sync_wait=1";
    Properties p = new Properties();
    p.put("javax.persistence.jdbc.url", jdbcUrl);
    p.put("javax.persistence.jdbc.user", JDBC_USER);
    p.put("javax.persistence.jdbc.password", JDBC_PASSWORD);
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU", p);

    EntityManager entityManager = emf.createEntityManager();

很好的解决方案。为我工作;无需努力,无需交易。缺点:我无法捕获异常(例如:首先检查变量是否存在,然后设置它——允许在不支持/使用此特定变量的系统上部署代码)。

【问题讨论】:

这对我来说非常有用。我遇到了类似的问题并选择了选项 2。我建议您在个人博客或其他地方发布此帖子。 【参考方案1】:

您还可以在每次调用 getConnection() 时使用方面来执行查询,这基本上适用于每个事务(方面是在调用之后设置的,以便我们拥有一个有效的连接对象):

@Component
@Aspect
public class CustomConnectionPreparer implements ConnectionPreparer

    @AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
    public Connection prepare(Connection connection) throws SQLException 
        // execute the query (also exception handling)
        try (Statement statement = connection.createStatement()) 
            statement.execute("SET SESSION wsrep_sync_wait = 1");
         catch (SQLException e) 
            throw e;
        
    
        return connection;
    

在您将连接返回给调用者之前,您执行查询,并且您应该始终设置该值。

【讨论】:

以上是关于JPA:如何设置 MySQL 会话变量?的主要内容,如果未能解决你的问题,请参考以下文章

使用MySQL会话变量实现窗口函数

如何打印当前设置的所有会话变量?

Java JPA:如何跨 HTTP 请求保持会话活动?

将两个会话变量添加到 MYSQL 查询中

为啥不会/如何对 PHP 会话变量进行排序而不是取消设置

如何在取消设置变量后修复会话未启动