JPA - EclipseLink - 如何更改默认模式

Posted

技术标签:

【中文标题】JPA - EclipseLink - 如何更改默认模式【英文标题】:JPA - EclipseLink - How to change default schema 【发布时间】:2011-03-13 18:33:32 【问题描述】:

我正在使用 weblogic 和 oracle 编写一个 Web 应用程序。 数据源是通过 JNDI 配置的,有一个受限的数据库用户可以 DML 到表中,但不能 DDL。您可能猜到了,该用户不是这些表的所有者,但他被授予访问权限。

假设他是 GUEST_USER

该应用程序正在使用 JPA + EclipseLink,并且已经定义了许多实体。我不想在每个实体类中都写入属性来更改模式。 我用这段代码尝试了 SessionCustomizer。

public class MyCustomizer implements SessionCustomizer

    @Override
    public void customize(Session session) throws Exception 

    session.executeNonSelectingSQL("ALTER SESSION SET CURRENT_SCHEMA = OWNERS_SCHEMA");
    

似乎有一些未初始化的东西,我得到一个空指针异常,我什至不确定这是否是在使用连接之前更改连接架构的方法。 有任何示例或想法吗?

提前感谢您的帮助!

【问题讨论】:

【参考方案1】:

我在查询数据库之前使用EJB,所以通过使用Interceptors,我可以通过查看当前经过身份验证的用户来设置EJB上下文中的架构。

然后,当我构建实体管理器时,我可以设置正确的架构。这样,通过在表名之前不指定 schema,PostgreSQL 将查看 search_path 以确定要查询的 schema。

<!-- language: lang-java -->

public class Interceptor 

    Logger logger = Logger.getLogger(Interceptor.class);

    /**
     * 
     * @param ctx is always null before being passed to EJB implementation. We always query database
     * for populating context data, making user's session accessible to all EJB implementations
     * @return
     * @throws Exception
     */
    @SuppressWarnings( "unchecked", "unused" )
    @AroundInvoke
    public Object intercept(InvocationContext ctx) throws Exception 
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        String ejbName = ctx.getMethod().getDeclaringClass().getSimpleName();
        String methodName = ctx.getMethod().getName();
        Boolean override_schema = false;
        String overridden_schema = "";

        logger.info("Intercepting " + ejbName + "." + methodName);

        if(auth != null) 

            UserDetails userDetails = (UserDetails)auth.getPrincipal();
            String username = userDetails.getUsername();

            Collection<SimpleGrantedAuthority> permList = (Collection<SimpleGrantedAuthority>) auth.getAuthorities();   
            List<String> permissions = new ArrayList<String>();

            for (SimpleGrantedAuthority authority : permList) 
                permissions.add(authority.getAuthority());
            


            Query query = getMasterEntityManager()
                            .createNativeQuery(
                "SQL for retrieving the schema by the current logged in user");

            query.setParameter("username", username);
            List<Object[]> result = null; //query.getResultList();

            if(result != null) 
                logger.info("Interceptor: context set for " + username);
                Object[] userObj = result.get(0);

                getContext().getContextData().put("username", username);
                getContext().getContextData().put("schema_name",(String)userObj[1]);
            
        

        return ctx.proceed();
      
     

然后当你构建实体管理器时,你可以设置你想要的架构。

【讨论】:

【参考方案2】:

您可以以编程方式进行。您可以为每个会话配置默认架构值。

public class MySessionCustomizer implements SessionCustomizer 

  private static String schemaName;

  public static void setSchemaName(String schemaName) 
      MySessionCustomizer.schemaName = schemaName;
  

  @Override
  public void customize(Session session) throws Exception 
      if (StringUtils.hasText(this.schemaName)) 
          session.getLogin().setTableQualifier(this.schemaName);
      
  

然后将会话定制器设置为实体管理器工厂属性:

PersistenceUnitProperties.SESSION_CUSTOMIZER 

例如

propertiesMap.put(PersistenceUnitProperties.SESSION_CUSTOMIZER, MySessionCustomizer.class.getName());

【讨论】:

是否有 JPA 标准的方法来做到这一点? 一种 JPA 标准方式是在注解 @Table 中使用参数模式 根据Eclipse User Guide,还有另一种注册定制器的方法-将其添加到persistence.xmlproperties标签中,例如:&lt;property name="eclipselink.session.customizer" value="com.company.customizer.MySessionCustomizer " /&gt;【参考方案3】:

如果所有实体都使用相同的架构,您可以使用 xml 映射文件来定义默认架构。

这样的东西应该可以工作(例如对于 JPA 2.0,将 schemaLocation 更改为 1.0)

orm.xml:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
    version="2.0">
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <schema>OWNERS_SCHEMA</schema>
        </persistence-unit-defaults>
    </persistence-unit-metadata>   
    . . .
</entity-mappings>

persistence.xml:

<persistence
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0" >
    <persistence-unit name="foo">
        . . .
        <mapping-file>orm.xml</mapping-file>
        . . .
    </persistence-unit>
</persistence>

【讨论】:

这很好但仍然不是动态的,如果我需要为指向两个差异数据库实例的同一个应用程序的两个版本更改架构怎么办? 此外,如果我使用 SET search_path TO ... 为特定的 EntityManager 设置架构,则将实体管理器传递给其他方法没有任何效果。我想 Wildfly(在我的情况下)采用以前使用公共默认模式创建的池连接之一

以上是关于JPA - EclipseLink - 如何更改默认模式的主要内容,如果未能解决你的问题,请参考以下文章

同一应用程序中的 JPA/Eclipselink 和 JDBC 连接

Java JPA(EclipseLink)如何在持久化实际实体之前接收下一个 GeneratedValue?

JPA 使用指南 /Eclipselink/JPA 实体生成器

JPA + EclipseLink + SAP云平台 = 运行在云端的数据库应用

JPA + EclipseLink + SAP云平台 = 运行在云端的数据库应用

使用 EclipseLink JPA 将 XML 类型存储到 PostgreSQL