从STRUTS2到JPA的调用过程的疑问?? 多谢解答!

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从STRUTS2到JPA的调用过程的疑问?? 多谢解答!相关的知识,希望对你有一定的参考价值。

在struts2的action中注入了service的对象,service是用@Service("userService")注释进行实例化的,那实例化类型是不是必须为@Scope("prototype")这种类型,担心在struts2的多线程、多并发访问的时候在service类的方法中会导致数据混乱(请大家帮我解释一下这点);,因为struts2不是单例的,那是不是service类也必须要是@Scope("prototype"),这样为每个请求都重新创建一个service类的对象,这样才不致于在多线程情况下导致数据可能混乱呢??????

代码如下:
STRUTS2 Action类

public class UserAction extends BaseAction

private static final long serialVersionUID = -2575509284575823496L;

@Resource(name = "userService")
private UserService userService;

private User user;
private List<Role> roleList;

public String add() throws Exception
if (CollectionUtils.isEmpty(roleList))
addActionError("请选择角色");
return INPUT;

user.setRoleSet(new HashSet<Role>(roleList));
userService.createUser(user);
return SUCCESS;



SERVICE类:
@Scope("") ??????????????????? 这里什么时候用singleton 或者 prototype????????????????????????????????
@Service("userService")
public class UserServiceImpl extends PersistService implements UserService

public void createUser(User user)
user.setPassword(MD5Utils.MD5Encode(user.getPassword()));
this.getJpaPersistence().persist(user);



在上面service类继承的PersistService 类中,也用spring注入了一个对数据库的通用的dao操作类(JpaPersistence jpaPersistence)
这样就可以在不同的service类中使用相同的dao操作类,这个通用的dao操作类是由spring的配置文件实例化的,配置如下:
<bean id="jpaPersistence" class="com.test.common.dao.support.JpaPersistenceImpl">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
那这个通用的dao操作类(JpaPersistence jpaPersistence)也就是singleton的,那这个通用的dao操作类在不同的service类中使用,会不会也像在STRUTS2的action中使用service类一样会导致数据混乱呢???

多谢解答!

我按我的理解回答:
(1)struts2的action不是以多线程的方式运行的,每个请求会创建一个新的action对象,action中的数据线程安全;
(2)如果Service对象拥有供多个action共享的数据,如Service有一个map属性,所有的action都可以更新其中的数据,则该service不是线程安全的,会出现数据不一致的现象;共享的属性可以上锁或用ThreadLocal解决问题。
一般Service很少需要共享可修改的属性,所以Service dao都适合做成单例。
(3)多个Service线程操作同一dao与(2)同理。
参考技术A 首先你没搞清楚基本概念
@Scope("prototype")这种类型 意思就是原型模式
如果不加这个注解,默认就是单例模式。
单例模式有个好处,不如service的对象
你在struts2中,不管有多少个action,你所注入的那个service都是同一个
因为每次请求都是一个新的action
Action必须要加@Scope("prototype")

如何从 Java 和 JPA 调用存储过程

【中文标题】如何从 Java 和 JPA 调用存储过程【英文标题】:How to call a stored procedure from Java and JPA 【发布时间】:2011-04-04 02:40:00 【问题描述】:

我正在编写一个简单的 Web 应用程序来调用存储过程并检索一些数据。 它是一个非常简单的应用程序,它与客户的数据库进行交互。我们传递员工 ID 和公司 ID,存储过程将返回员工详细信息。

Web 应用程序无法更新/删除数据并且正在使用 SQL Server。

我正在 Jboss AS 中部署我的 Web 应用程序。我应该使用 JPA 访问存储过程还是 CallableStatement.在这种情况下使用 JPA 的任何优势。

还有什么是调用这个存储过程的sql语句。我以前从未使用过存储过程,我正在努力解决这个问题。 Google 帮不上什么忙。

这是存储过程:

CREATE procedure getEmployeeDetails (@employeeId int, @companyId int)
as
begin
    select firstName, 
           lastName, 
           gender, 
           address
      from employee et
     where et.employeeId = @employeeId
       and et.companyId = @companyId
end

更新:

对于使用 JPA 调用存储过程时遇到问题的其他人。

Query query = em.createNativeQuery("call getEmployeeDetails(?,?)",
                                   EmployeeDetails.class)           
                                   .setParameter(1, employeeId)
                                   .setParameter(2, companyId);

List<EmployeeDetails> result = query.getResultList();

我注意到的事情:

    参数名称对我不起作用,因此请尝试使用参数索引。 更正sql语句call sp_name(?,?)而不是call sp_name(?,?) 如果存储过程返回一个结果集,即使您只知道一行,getSingleResult 也不起作用 传递resultSetMapping 名称或结果类详细信息

【问题讨论】:

您不能在 native 查询中使用命名参数。仅 JPQL 查询支持命名参数。 (如果您更喜欢命名参数,您可以编写自己的类来将命名参数转换为编号参数。) 我一直将命名参数与 createNativeQueries 一起使用,从未遇到任何问题。我刚刚查看了我一直在使用的当前系统,并且有大量带有命名参数的本机查询。您能为我们的肯定提供一些参考吗?我们的设置是 JPA 2 和 Hibernate 4+。 【参考方案1】:

JPA 2.1 现在支持存储过程,请阅读 Java 文档 here。

例子:

StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("sales_tax");
// set parameters
storedProcedure.registerStoredProcedureParameter("subtotal", Double.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("tax", Double.class, ParameterMode.OUT);
storedProcedure.setParameter("subtotal", 1f);
// execute SP
storedProcedure.execute();
// get result
Double tax = (Double)storedProcedure.getOutputParameterValue("tax");

查看详细示例here。

【讨论】:

【参考方案2】:

我正在 Jboss AS 中部署我的 Web 应用程序。我应该使用 JPA 来访问存储过程还是 CallableStatement。在这种情况下使用 JPA 的任何优势。

JPA 并不真正支持它,但它是 doable。我还是不会走这条路:

使用 JPA 只是在某些 bean 中映射存储过程调用的结果实在是大材小用, 尤其是考虑到 JPA 并不适合调用存储过程(语法会非常冗长)。

因此,我宁愿考虑使用Spring support for JDBC data access,或者像MyBatis 这样的数据映射器,或者考虑到您的应用程序的简单性,使用原始JDBC 和CallableStatement。实际上,JDBC 可能是我的选择。这是一个基本的启动示例:

CallableStatement cstmt = con.prepareCall("call getEmployeeDetails(?, ?)");
cstmt.setInt("employeeId", 123);
cstmt.setInt("companyId", 456);
ResultSet rs = cstmt.executeQuery();

参考

JDBC 文档:Java SE 6

【讨论】:

如answer below 中所述,它受支持 - 您可能需要编辑【参考方案3】:

您需要将参数传递给存储过程。

它应该像这样工作:

    List result = em
      .createNativeQuery("call getEmployeeDetails(:employeeId,:companyId)")
      .setParameter("emplyoyeeId", 123L)
      .setParameter("companyId", 456L)
      .getResultList();

更新:

也许不应该。

在书EJB3 in Action中,它在第383页上写着JPA does not support stored procedures(页面只是预览,您没有获得全文,整本书可在多个地方下载,包括@987654323 @,我不知道这是否合法)。

反正正文是这样的:

JPA 和数据库存储过程

如果您是 SQL 的忠实粉丝,您可能会 愿意利用自己的力量 数据库存储过程。 不幸的是,JPA 不支持 存储过程,你必须 取决于的专有功能 您的持久性提供者。然而, 您可以使用简单的存储函数 (没有输出参数)与原生 SQL 查询。

【讨论】:

我试过并收到此错误消息:java.sql.SQLException: Incorrect syntax near '@P0'。 应该是“call getEmployeeDetails(:employeeId,:companyId)”,对于SQL server,它必须有大括号。 @Vedran 是的。我只对参数设置部分感兴趣【参考方案4】:

    对于像这样使用 IN/OUT 参数的简单存储过程

    CREATE OR REPLACE PROCEDURE count_comments (  
       postId IN NUMBER,  
       commentCount OUT NUMBER )  
    AS 
    BEGIN 
        SELECT COUNT(*) INTO commentCount  
        FROM post_comment  
        WHERE post_id = postId; 
    END;
    

    您可以按如下方式从 JPA 中调用它:

    StoredProcedureQuery query = entityManager
        .createStoredProcedureQuery("count_comments")
        .registerStoredProcedureParameter(1, Long.class, 
            ParameterMode.IN)
        .registerStoredProcedureParameter(2, Long.class, 
            ParameterMode.OUT)
        .setParameter(1, 1L);
    
    query.execute();
    
    Long commentCount = (Long) query.getOutputParameterValue(2);
    

    对于使用SYS_REFCURSOR OUT 参数的存储过程:

    CREATE OR REPLACE PROCEDURE post_comments ( 
       postId IN NUMBER, 
       postComments OUT SYS_REFCURSOR ) 
    AS 
    BEGIN
        OPEN postComments FOR
        SELECT *
        FROM post_comment 
        WHERE post_id = postId; 
    END;
    

    你可以这样称呼它:

    StoredProcedureQuery query = entityManager
        .createStoredProcedureQuery("post_comments")
        .registerStoredProcedureParameter(1, Long.class, 
             ParameterMode.IN)
        .registerStoredProcedureParameter(2, Class.class, 
             ParameterMode.REF_CURSOR)
        .setParameter(1, 1L);
    
    query.execute();
    
    List<Object[]> postComments = query.getResultList();
    

    对于如下所示的 SQL 函数:

    CREATE OR REPLACE FUNCTION fn_count_comments ( 
        postId IN NUMBER ) 
        RETURN NUMBER 
    IS
        commentCount NUMBER; 
    BEGIN
        SELECT COUNT(*) INTO commentCount 
        FROM post_comment 
        WHERE post_id = postId; 
        RETURN( commentCount ); 
    END;
    

    你可以这样称呼它:

    BigDecimal commentCount = (BigDecimal) entityManager
    .createNativeQuery(
        "SELECT fn_count_comments(:postId) FROM DUAL"
    )
    .setParameter("postId", 1L)
    .getSingleResult();
    

    至少在使用 Hibernate 4.x 和 5.x 时,因为 JPA StoredProcedureQuery 不适用于 SQL FUNCTIONS。

关于在使用JPA和Hibernate时如何调用存储过程和函数的更多细节,请查看以下文章

How to call Oracle stored procedures and functions with JPA and Hibernate How to call SQL Server stored procedures and functions with JPA and Hibernate How to call PostgreSQL functions (stored procedures) with JPA and Hibernate How to call MySQL stored procedures and functions with JPA and Hibernate

【讨论】:

我不断收到“调用...的参数数量或类型错误”错误消息。我意识到我在打电话给createNativeQuery。我切换到createStoredProcedureQuery。然后,瞧!【参考方案5】:

如何使用 JPA 检索存储过程输出参数(2.0 需要 EclipseLink 导入而 2.1 不需要)

尽管这个答案确实详细说明了从存储过程返回记录集, 我在这里发帖,因为我花了很长时间才弄明白,这个帖子帮助了我。

我的应用程序使用的是 Eclipselink-2.3.1,但我将强制升级到 Eclipselink-2.5.0,因为 JPA 2.1 对存储过程有更好的支持。

使用 EclipseLink-2.3.1/JPA-2.0:依赖于实现

此方法需要从“org.eclipse.persistence”导入 EclipseLink 类,因此它是特定于 Eclipselink 实现的。

我在“http://www.yenlo.nl/en/calling-oracle-stored-procedures-from-eclipselink-with-multiple-out-parameters”找到它。

StoredProcedureCall storedProcedureCall = new StoredProcedureCall();
storedProcedureCall.setProcedureName("mypackage.myprocedure");
storedProcedureCall.addNamedArgument("i_input_1"); // Add input argument name.
storedProcedureCall.addNamedOutputArgument("o_output_1"); // Add output parameter name.
DataReadQuery query = new DataReadQuery();
query.setCall(storedProcedureCall);
query.addArgument("i_input_1"); // Add input argument names (again);
List<Object> argumentValues = new ArrayList<Object>();
argumentValues.add("valueOf_i_input_1"); // Add input argument values.
JpaEntityManager jpaEntityManager = (JpaEntityManager) getEntityManager();
Session session = jpaEntityManager.getActiveSession();
List<?> results = (List<?>) session.executeQuery(query, argumentValues);
DatabaseRecord record = (DatabaseRecord) results.get(0);
String result = String.valueOf(record.get("o_output_1")); // Get output parameter

使用 EclipseLink-2.5.0/JPA-2.1:独立于实现(已在此线程中记录)

此方法独立于实现(不需要 Eclipslink 导入)。

StoredProcedureQuery query = getEntityManager().createStoredProcedureQuery("mypackage.myprocedure");
query.registerStoredProcedureParameter("i_input_1", String.class, ParameterMode.IN);
query.registerStoredProcedureParameter("o_output_1", String.class, ParameterMode.OUT);
query.setParameter("i_input_1", "valueOf_i_input_1");
boolean queryResult = query.execute();
String result = String.valueOf(query.getOutputParameterValue("o_output_1"));

【讨论】:

啊,我的眼睛受伤了。这并不比 JDBC 好多少,是吗? 哈哈,没错。然而,使用这些东西的好处是您不必输入大量代码来获取数据对象类,并且您不必将所有数据从 recordSet 传输到数据类中.仍然有一个数据对象(实体),但 Eclipse 向导会为您生成它。 是的,你可以。但我是以jOOQ 的开发者的身份说的,一切都是在这里生成的。剩下要做的就是实际调用过程/函数。 你真的尝试过底部的例子(独立于实现)吗?我尝试了它,不同之处在于该过程是在xml 文件中定义的,但它不起作用。我无法读取OUT 参数。 不知何故对于 JPA - 2.1 实现,命名参数对我不起作用。相反,我必须在存储过程中传递它们的位置索引,并成功地获得输出参数的结果。当我有存储过程返回多个结果集时就是这种情况。对于 1 ResultSet,我只使用了 @Query【参考方案6】:

对我来说,只有以下内容适用于 Oracle 11g 和 Glassfish 2.1 (Toplink):

Query query = entityManager.createNativeQuery("BEGIN PROCEDURE_NAME(); END;");
query.executeUpdate();

带有花括号的变体导致 ORA-00900。

【讨论】:

在 Oracle 11g 上为我工作,休眠 JPA 提供程序。 这让我们摆脱了巨大的麻烦。我们使用的是 java6、oracle11g、Jboss6、Hibernate。谢谢@Chornyi。【参考方案7】:

如果使用 EclipseLink,您可以使用 @NamedStoredProcedureQuery 或 StoreProcedureCall 来执行任何存储过程,包括带有输出参数或输出游标的存储过程。还提供对存储函数和 PLSQL 数据类型的支持。

看, http://en.wikibooks.org/wiki/Java_Persistence/Advanced_Topics#Stored_Procedures

【讨论】:

哪个版本的 EclipseLink 有 EntityManager.createNamedStoredProcedureQuery()?【参考方案8】:

以下对我有用:

Query query = em.createNativeQuery("BEGIN VALIDACIONES_QPAI.RECALC_COMP_ASSEMBLY('X','X','X',0); END;");
query.executeUpdate();

【讨论】:

OUT 和 INOUT 参数在使用此 API 时不起作用。见en.wikibooks.org/wiki/Java_Persistence/…【参考方案9】:

对于 Sql Srver 可能不一样,但对于使用 oracle 和 eclipslink 的人来说,它对我有用

例如:具有一个 IN 参数(CHAR 类型)和两个 OUT 参数(NUMBER 和 VARCHAR)的过程

在persistence.xml中声明持久化单元:

<persistence-unit name="presistanceNameOfProc" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/DataSourceName</jta-data-source>
    <mapping-file>META-INF/eclipselink-orm.xml</mapping-file>
    <properties>
        <property name="eclipselink.logging.level" value="FINEST"/>
        <property name="eclipselink.logging.logger" value="DefaultLogger"/>
        <property name="eclipselink.weaving" value="static"/>
        <property name="eclipselink.ddl.table-creation-suffix" value="JPA_STORED_PROC" />
    </properties>
</persistence-unit>

并在eclipselink-orm.xml中声明proc的结构

<?xml version="1.0" encoding="UTF-8"?><entity-mappings version="2.0"
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">
<named-stored-procedure-query name="PERSIST_PROC_NAME" procedure-name="name_of_proc" returns-result-set="false">
    <parameter direction="IN" name="in_param_char" query-parameter="in_param_char" type="Character"/>
    <parameter direction="OUT" name="out_param_int" query-parameter="out_param_int" type="Integer"/>
    <parameter direction="OUT" name="out_param_varchar" query-parameter="out_param_varchar" type="String"/>
</named-stored-procedure-query>

在代码中你只需要像这样调用你的过程:

try 
        final Query query = this.entityManager
                .createNamedQuery("PERSIST_PROC_NAME");
        query.setParameter("in_param_char", 'V'); 
        resultQuery = (Object[]) query.getSingleResult();

     catch (final Exception ex) 
        LOGGER.log(ex);
        throw new TechnicalException(ex);
    

获取两个输出参数:

Integer myInt = (Integer) resultQuery[0];
String myStr =  (String) resultQuery[1];

【讨论】:

【参考方案10】:

这对我有用。

@Entity
@Table(name="acct")
@NamedNativeQueries(
 @NamedNativeQuery(callable=true, name="Account.findOne", query="call sp_get_acct(?), resultClass=Account.class))
public class Account
 // Code 

注意:以后如果你决定使用 findOne 的默认版本,那么只需注释 NamedNativeQueries 注释,JPA 将切换到默认版本

【讨论】:

如果我想在特定包中调用过程,我应该这样调用:调用package.procedure?【参考方案11】:

如果您有实体经理,此答案可能会有所帮助

我有一个存储过程来创建下一个数字,并且在服务器端我有接缝框架。

客户端

 Object on = entityManager.createNativeQuery("EXEC getNextNmber").executeUpdate();
        log.info("New order id: " + on.toString());

数据库端(SQL server)我有一个名为getNextNmber的存储过程

【讨论】:

executeUpdate() 返回整数。确定您收到 sproc 的输出吗?【参考方案12】:

您可以在您的存储库中使用@Query(value = "call PROC_TEST()", nativeQuery = true)。这对我有用。

注意:使用''和'',否则不起作用。

【讨论】:

【参考方案13】:

JPA 2.0 不支持 RETURN 值,只支持调用。

我的解决方案是。创建一个调用 PROCEDURE 的 FUNCTION。

因此,在 JAVA 代码中,您执行一个调用 oracle FUNCTION 的 NATIVE QUERY。

【讨论】:

【参考方案14】:

调用存储过程可以使用java.sql包中的Callable Statement。

【讨论】:

感谢您的回复。所以可调用语句的 sql 将是 ? = call getEmployeeDetails(?,?) 或者需要指定所有输出参数【参考方案15】:

试试这个代码:

return em.createNativeQuery("call getEmployeeDetails(?,?)",
                               EmployeeDetails.class)           
                               .setParameter(1, employeeId)
                               .setParameter(2, companyId).getResultList();

【讨论】:

【参考方案16】:

persistence.xml

 <persistence-unit name="PU2" transaction-type="RESOURCE_LOCAL">
<non-jta-data-source>jndi_ws2</non-jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>

codigo java

  String PERSISTENCE_UNIT_NAME = "PU2";
    EntityManagerFactory factory2;
    factory2 = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);

    EntityManager em2 = factory2.createEntityManager();
    boolean committed = false;
    try 

        try 
            StoredProcedureQuery storedProcedure = em2.createStoredProcedureQuery("PKCREATURNO.INSERTATURNO");
            // set parameters
            storedProcedure.registerStoredProcedureParameter("inuPKEMPRESA", BigDecimal.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("inuPKSERVICIO", BigDecimal.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("inuPKAREA", BigDecimal.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("isbCHSIGLA", String.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("INUSINCALIFICACION", BigInteger.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("INUTIMBRAR", BigInteger.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("INUTRANSFERIDO", BigInteger.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("INTESTADO", BigInteger.class, ParameterMode.IN);
            storedProcedure.registerStoredProcedureParameter("inuContador", BigInteger.class, ParameterMode.OUT);

            BigDecimal inuPKEMPRESA = BigDecimal.valueOf(1);
            BigDecimal inuPKSERVICIO = BigDecimal.valueOf(5);
            BigDecimal inuPKAREA = BigDecimal.valueOf(23);
            String isbCHSIGLA = "";
            BigInteger INUSINCALIFICACION = BigInteger.ZERO;
            BigInteger INUTIMBRAR = BigInteger.ZERO;
            BigInteger INUTRANSFERIDO = BigInteger.ZERO;
            BigInteger INTESTADO = BigInteger.ZERO;
            BigInteger inuContador = BigInteger.ZERO;

            storedProcedure.setParameter("inuPKEMPRESA", inuPKEMPRESA);
            storedProcedure.setParameter("inuPKSERVICIO", inuPKSERVICIO);
            storedProcedure.setParameter("inuPKAREA", inuPKAREA);
            storedProcedure.setParameter("isbCHSIGLA", isbCHSIGLA);
            storedProcedure.setParameter("INUSINCALIFICACION", INUSINCALIFICACION);
            storedProcedure.setParameter("INUTIMBRAR", INUTIMBRAR);
            storedProcedure.setParameter("INUTRANSFERIDO", INUTRANSFERIDO);
            storedProcedure.setParameter("INTESTADO", INTESTADO);
            storedProcedure.setParameter("inuContador", inuContador);

            // execute SP
            storedProcedure.execute();
            // get result

            try 
                long _inuContador = (long) storedProcedure.getOutputParameterValue("inuContador");
                varCon = _inuContador + "";
             catch (Exception e) 
             
         finally 

        
     finally 
        em2.close();
    

【讨论】:

请不要犹豫,为您的答案添加任何评论(纯代码除外)。【参考方案17】:

从 JPA 2.1 开始,JPA 支持使用动态 StoredProcedureQuery 和声明性 @NamedStoredProcedureQuery 调用存储过程。

【讨论】:

以上是关于从STRUTS2到JPA的调用过程的疑问?? 多谢解答!的主要内容,如果未能解决你的问题,请参考以下文章

Struts2 Spring JPA 整合时报错:No bean named 'entityManagerFactory' is defined ,请问各位是怎么解决

如何从 Java 和 JPA 调用存储过程

Struts2 JPA 验证错误如何转发到操作而不丢失 ActionErrors 消息?

JPA 2.1在sql server中调用存储过程

Qt例子,线程间通信,如何在线程外部对线程进行控制,问题请看问题补充,多谢了先

java 怎么根据httpPost 和httpClient 等,传图片到服务器!