Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/367.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JPA-通过编程方式在序列中递增数字字段_Java_Sql_Hibernate_Jpa - Fatal编程技术网

Java JPA-通过编程方式在序列中递增数字字段

Java JPA-通过编程方式在序列中递增数字字段,java,sql,hibernate,jpa,Java,Sql,Hibernate,Jpa,我有一个JPA2 web应用程序(Struts 2、Hibernate 4仅作为JPA实现) 当前的要求是向现有实体添加一个(非id)数字顺序字段,该字段仅为某些行填充。在插入新行时,根据特定条件,我需要将新字段设置为其最高值+1或NULL 例如: ID NEW_FIELD DESCRIPTION -------------------------------- 1 1 bla bla 2 bla b

我有一个JPA2 web应用程序(Struts 2、Hibernate 4仅作为JPA实现)

当前的要求是向现有实体添加一个(非id)数字顺序字段,该字段仅为某些行填充。在插入新行时,根据特定条件,我需要将新字段设置为
其最高值+1
NULL

例如:

ID     NEW_FIELD     DESCRIPTION
--------------------------------
1          1           bla bla
2                      bla bla       <--- unmatched: not needed here
3                      bla bla       <--- unmatched: not needed here
4          2           bla bla
5          3           bla bla
6          4           bla bla
7                      bla bla       <--- unmatched: not needed here
8          5           bla bla
9                      bla bla       <--- unmatched: not needed here
10         6           bla bla
但我不知道如何用JPA2实现它

我知道我可以定义回调方法,但不鼓励从内部执行某些特定操作:

3.5实体侦听器和回调方法 -生命周期回调可以调用JNDI、JDBC、JMS和企业bean
-通常,可移植应用程序的生命周期方法不应调用EntityManager或查询操作,也不应访问其他实体 实例,或修改同一持久性中的关系 context[43]生命周期回调方法可能会修改 对其调用的实体的非关系状态

[43]此类操作的语义可以标准化 在本规范的未来版本中

总之,JDBC(!)和EJB是,EntityManager和其他实体不是


编辑 我试图实现@anttix答案中描述的解决方案,但我遇到了一些问题,请纠正我的错误

表格

MyTable
-------------------------
ID            number (PK)
NEW_FIELD     number
DESCRIPTION   text
主要实体

@Entity
@Table(name="MyTable")
public class MyEntity implements Serializable {

    @Id
    @SequenceGenerator(name="seq_id", sequenceName="seq_id", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_id")
    private Long id;

    @OneToOne(cascade= CascadeType.PERSIST) 
    private FooSequence newField;

    private String description

    /* Getters and Setters */
}
@Entity
public class FooSequence {

    @Id
    @SequenceGenerator(name="seq_foo", sequenceName="seq_foo", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_foo")
    private Long value;

    /* Getter and Setter */
}
子实体

@Entity
@Table(name="MyTable")
public class MyEntity implements Serializable {

    @Id
    @SequenceGenerator(name="seq_id", sequenceName="seq_id", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_id")
    private Long id;

    @OneToOne(cascade= CascadeType.PERSIST) 
    private FooSequence newField;

    private String description

    /* Getters and Setters */
}
@Entity
public class FooSequence {

    @Id
    @SequenceGenerator(name="seq_foo", sequenceName="seq_foo", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_foo")
    private Long value;

    /* Getter and Setter */
}
DAO

myEntity.setNewField(new FooSequence());
entityManager.persist(myEntity);
例外情况

原因:javax.transaction.RollbackException:ARJUNA016053:无法提交事务

[……]

原因:javax.persistence.PersistenceException:org.hibernate.exception.sqlgrammareexception:错误:关系“new_字段”不存在

[……]

原因:org.hibernate.exception.sqlgrammareexception:错误:关系“新字段”不存在

[……]

原因:org.postgresql.util.PSQLException:错误:关系“新建字段”不存在

我做错了什么?我是JPA2的新手,我从未使用过与物理表无关的实体。。。这种方法对我来说是全新的

我想我需要把
@列
定义放在某个地方:JPA怎么可能知道
新字段
列(映射到数据库上的
新字段
)是通过
FooSequence
实体的
属性检索的

拼图的一些部分不见了


编辑

如评论中所述,这是
persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">

    <persistence-unit name="MyService"  transaction-type="JTA">

        <jta-data-source>java:jboss/datasources/myDS</jta-data-source>      

        <properties>             

            <property name="hibernate.dialect" 
                     value="org.hibernate.dialect.PostgreSQLDialect" />

            <property name="hibernate.ejb.naming_strategy" 
                     value="org.hibernate.cfg.ImprovedNamingStrategy"/>

            <property name="hibernate.query.substitutions" 
                     value="true 'Y', false 'N'"/>           

         <property name="hibernate.show_sql" value="true" />
         <property name="format_sql"         value="true" />
         <property name="use_sql_comments"   value="true" />

        </properties>

    </persistence-unit>
</persistence>

java:jboss/datasources/myDS

一种可能的解决方案是使用一个单独的实体及其自己的表,该表将仅封装新字段,并具有与该实体的OneToOne映射。然后,仅当遇到需要附加序列号的对象时,才会实例化新实体。然后可以使用任何生成器策略来填充它

@Entity
public class FooSequence {
    @Id
    @GeneratedValue(...)
    private Long value;
}

@Entity 
public class Whatever {
    @OneToOne(...)
    private FooSequnce newColumn;
}
见:

gradle 1.11可运行SSCCE(使用Spring Boot):

src/main/java/JpaMultikeyDemo.java

import java.util.List;
import javax.persistence.*;
import lombok.Data;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;

@Configuration
@EnableTransactionManagement
@EnableAutoConfiguration
public class JpaMultikeyDemo {
    @Entity @Data
    public static class FooSequence {
        @Id @GeneratedValue private Long value;
    }

    @Entity @Data
    public static class FooEntity {
        @Id @GeneratedValue private Long id;
        @OneToOne           private FooSequence sequence;
    }

    @PersistenceContext
    EntityManager em;

    @Transactional
    public void runInserts() {
        // Create ten objects, half with a sequence value
        for(int i = 0; i < 10; i++) {
            FooEntity e1 = new FooEntity();
            if(i % 2 == 0) {
                FooSequence s1 = new FooSequence();
                em.persist(s1);
                e1.setSequence(s1);
            }
            em.persist(e1);
        }
    }

    public void showAll() {
        String q = "SELECT e FROM JpaMultikeyDemo$FooEntity e";
        for(FooEntity e: em.createQuery(q, FooEntity.class).getResultList())
            System.out.println(e);
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(JpaMultikeyDemo.class);
        context.getBean(JpaMultikeyDemo.class).runInserts();
        context.getBean(JpaMultikeyDemo.class).showAll();
        context.close();
    }
}

另请参见:

对于某些AOP来说,这似乎是一个很好的例子。首先创建一个自定义字段注释
@CustomSequenceGeneratedValue
,然后用它在实体上注释字段:

public class MyEntity {
...
    @CustomSequenceGeneratedValue
    private Long generatedValue;

    public void setGeneratedValue(long generatedValue) {

    }
}
然后创建一个方面以增加生成的值:

@Aspect
public class CustomSequenceGeneratedValueAspect {

    @PersistenceContext 
    private EntityManager em;

    @Before("execution(* com.yourpackage.dao.SomeDao.*.*(..))")
    public void beforeSaving(JoinPoint jp) throws Throwable {
        Object[] args = jp.getArgs();
        MethodSignature ms = (MethodSignature) jp.getSignature();
        Method m = ms.getMethod();

        Annotation[][] parameterAnnotations = m.getParameterAnnotations();

        for (int i = 0; i < parameterAnnotations.length; i++) {
            Annotation[] annotations = parameterAnnotations[i];
            for (Annotation annotation : annotations) {
                if (annotation.annotationType() == CustomSequenceGeneratedEntity.class) {
                       ... find generated properties run query and call setter ...

                      ... Query query = em.createNativeQuery("select MY_SEQUENCE.NEXTVAL from dual");
                }
            }
        }
    } 
}
@方面
公共类CustomSequenceGeneratedValueAspect{
@持久上下文
私人实体管理者;
@在(“execution(*com.yourpackage.dao.SomeDao.*.*(…))之前)
保存前公共无效(JoinPoint jp)抛出可丢弃{
对象[]args=jp.getArgs();
MethodSignature ms=(MethodSignature)jp.getSignature();
方法m=ms.getMethod();
注释[][]parameterAnnotations=m.getParameterAnnotations();
for(int i=0;i

然后,使用
扫描方面,并将其应用于此类型的任何Spring DAO保存实体。方面将基于一个序列以透明的方式为用户填充序列生成的值。

您提到的开放使用JDBC。下面是如何将实体回调与JdbcTemplate一起使用,该示例使用Postgres的语法来选择序列中的下一个值,只需对其进行更新,以便为数据库使用正确的语法即可

将其添加到实体类:

@javax.persistence.EntityListeners(com.example.MyEntityListener.class)
下面是侦听器实现(
@Qualifier
required=true
是它工作所必需的):


我用来保持简单的黑客解决方案如下:

MyEntity myEntity = new MyEntity();
myEntity.setDescription("blabla");
em.persist(myEntity);
em.flush(myEntity);
myEntity.setNewField(getFooSequence());
事务处理的完整代码(“伪代码”,我直接写在上面,因此可能会有拼写错误)如下所示:

实体

@Entity
@Table(name="MyTable")
public class MyEntity implements Serializable {

    @Id
    @SequenceGenerator(name="seq_id", sequenceName="seq_id", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_id")
    private Long id;

    private Long newField; // the optional sequence
    private String description
    /* Getters and Setters */
}
主EJB:

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER) // default
public class MainEjb implements MainEjbLocalInterface {

    @Inject 
    DaoEjbLocalInterface dao;

    // Create new session, no OSIV here
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public Long insertMyEntity(boolean myCondition) throws Exception {

        try {
            MyEntity myEntity = dao.insertMyEntity(); 
            // if this break, no FooSequence will be generated

            doOtherStuff();
            // Do other non-database stuff that can break here. 
            // If they break, no FooSequence will be generated, 
            // and no myEntity will be persisted.                                

            if (myCondition) {
                myEntity.setNewField(dao.getFooSequence());
                // This can't break (it would have break before). 
                // But even if it breaks, no FooSequence will be generated,
                // and no myEntity will be persisted.
            }
        } catch (Exception e){
            getContext().setRollbackOnly();
            log.error(e.getMessage(),e);
            throw new MyException(e);
        }    
    }
}
DAO EJB

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER) // default
public class DaoEjb implements DaoEjbLocalInterface {

    @PersistenceContext( unitName="myPersistenceUnit")
    EntityManager em;

    // default, use caller (MainEJB) session
    @TransactionAttribute(TransactionAttributeType.REQUIRED) 
    public MyEntity insertMyEntity() throws Exception{
        MyEntity myEntity = new MyEntity();
        myEntity.setDescription("blabla");
        em.persist(myEntity);
        em.flush(); // here it will break in case of database errors, 
                    // eg. description value too long for the column.
                    // Not yet committed, but already "tested".
        return myEntity;
    }

    // default, use caller (MainEJB) session
    @TransactionAttribute(TransactionAttributeType.REQUIRED) 
    public Long getFooSequence() throws Exception {
        Query query = em.createNativeQuery("SELECT nextval('seq_foo')");
        return ((BigInteger) query.getResultList().get(0)).longValue();
    }
}
这将保证FooSequence生成过程中不会出现任何间隙

唯一的缺点,在我的用例中我根本不关心,就是FooSequence和@Id序列不同步,所以两个并发插入可能有“反转”的FooSequence值,与它们的到达顺序有关,例如

ID  NEW FIELD
-------------
 1      2
 2      1

谢谢,在问我的问题之前我已经找到了答案,但我不确定这是否是最好的方式。我已经开始了