JPA - 以编程方式通过序列增加数字字段 [英] JPA - Increment a numeric field through a sequence programmatically

查看:125
本文介绍了JPA - 以编程方式通过序列增加数字字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个JPA 2 web应用程序(Struts 2,Hibernate 4仅作为JPA实现)。
$ b

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



例如:

  ID NEW_FIELD DESCRIPTION 
--------------------------------
1 1 bla bla
2 bla bla <--- unmatched:这里不需要
3 bla bla <---不匹配:这里不需要
4 2 bla bla
5 3 bla bla
6 4 bla bla
bla bla <---无与伦比:这里不需要
8 5 bla bla
9 bla bla <---无与伦比:这里不需要
10 6 bla bla

在旧的SQL中,它会是这样的:

INSERT INTO myTable(
id,
new_fie ld,
描述
)VALUES(
myIdSequence.nextVal,
(CASE myCondition
当真
然后myNewFieldSequence.nextVal
ELSE NULL
END),
'Lorem Ipsum等等......'

但我不知道如何使用JPA 2来实现它。



我知道我可以定义回调方法,但 JSR-000317 Eval 2.0 Eval的持久性规范阻碍了一些特定的操作从它里面:


3.5实体监听器和回调方法

- 生命周期回调可以调用JNDI,JDBC,JMS和企业bean。一般而言,可移植应用程序的生命周期方法不应调用EntityManager或Query操作,访问其他实体
实例或修改其中的关系相同的持久性
上下文
[43] 生命周期回调方法可以修改调用它的实体的
非关系状态。
>



[43] 在这个规范的未来版本中,这些操作的语义可以标准化为
。 / b>

总结,对于JDBC(!)和EJB来说,对于EntityManager和其他实体来说不是。






编辑



我试图实现从@ anttix,但我遇到了一些问题,所以请纠正我在哪里我错了。

  MyTable 
-------------------------
ID号码(PK)
NEW_FIELD号码
描述文本

主要实体

  @Entity 
@Table(name =MyTable)
public class MyEntity实现Serializable {

@Id
@SequenceGenerator(name =seq_id,sequenceName =seq_id,allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator =seq_id)
私人长ID;

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

私人字符串描述

/ *吸气器和接触器* /
}

子实体

  @Entity 
public class FooSequence {

@Id
@SequenceGenerator(name =seq_foo,sequenceName =seq_foo,allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator =seq_foo)
私人长期价值;

/ * Getter and Setter * /
}

DAO

  myEntity.setNewField(new FooSequence()); 
entityManager.persist(myEntity);

例外

< blockquote>

导致:javax.transaction.RollbackException:ARJUNA016053:无法提交事务。

[...]



导致:javax.persistence.PersistenceException:org.hibernate.exception.SQLGrammarException:ERROR:关系new_field不存在



[...]



导致:org.hibernate.exception.SQLGrammarException:错误:关系new_field不存在


$ b

引起:org.postgresql.util.PSQLException:错误:关系new_field不存在


我做错了什么?我对JPA 2非常陌生,并且从未使用过与物理表关联的实体......这种方法对我来说是全新的。



我想我需要把 @Column 定义放在某处:JPA怎么可能知道 newField 列(通过 ImprovedNamingStrategy 映射到 new_field 通过 FooSequence 实体的属性检索数据库上的code>

缺少一些难题。






编辑



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

 <?xml version =1.0encoding =UTF-8?> 
< persistence version =2.0xmlns =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 =MyServicetransaction-type =JTA>

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

<属性>

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

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

value =true'Y',false'N'/>

< property name =hibernate.show_sqlvalue =true/>
< property name =format_sqlvalue =true/>
< property name =use_sql_commentsvalue =true/>

< / properties>

< / persistence-unit>
< /余辉>


解决方案

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

  @Entity 
公共类FooSequence {
@ Id
@GeneratedValue(...)
私有长值;
}

@Entity
公共类别无论{
@OneToOne(...)
私人FooSequnce newColumn;

请参阅:



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

src / main / java / JpaMultikeyDemo.java
$ b

  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长整型值;
}

@Entity @Data
public static class FooEntity {
@Id @GeneratedValue private Long id;
@OneToOne私人FooSequence序列;
}

@PersistenceContext
EntityManager em;

@Transactional
public void runInserts(){
//创建十个对象,一半带序列值
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();
}
}

build.gradle

  apply plugin:'java'
defaultTasks'execute'
$ b $ repositories {
mavenCentral()
maven {urlhttp://repo.spring.io/libs-milestone}
}

依赖关系{
compileorg.springframework.boot:spring-boot -starter-data-jpa:1.0.0.RC5
compileorg.projectlombok:lombok:1.12.6
compilecom.h2database:h2:1.3.175
}

任务执行(类型:JavaExec){
main =JpaMultikeyDemo
classpath = sourceSets.main.runtimeClasspath
}

另请参阅: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-configure-datasource


I have a JPA 2 web application (Struts 2, Hibernate 4 as JPA implementation only).

The current requirement is to add a (non-id) numeric sequential field, filled for certain rows only, to an existing entity. When inserting a new row, based on a certain condition, I need to set the new field to its highest value + 1 or to NULL.

For example:

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

In the good old SQL, it would be something like:

INSERT INTO myTable (
    id, 
    new_field, 
    description
) VALUES (
    myIdSequence.nextVal, 
    (CASE myCondition
        WHEN true 
        THEN myNewFieldSequence.nextVal
        ELSE NULL
    END),
    'Lorem Ipsum and so on....'
)

But I've no clue on how to achieve it with JPA 2.

I know I can define callbacks methods, but JSR-000317 Persistence Specification for Eval 2.0 Eval discourages some specific operations from inside it:

3.5 Entity Listeners and Callback Methods
- Lifecycle callbacks can invoke JNDI, JDBC, JMS, and enterprise beans.
- In general, the lifecycle method of a portable application should not invoke EntityManager or Query operations, access other entity instances, or modify relationships within the same persistence context.[43] A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.

[43] The semantics of such operations may be standardized in a future release of this specification.

Summarizing, yes to JDBC (!) and EJB, no to EntityManager and other Entities.


EDIT

I'm trying to achieve the solution described in the answer from @anttix, but I'm encoutering some problem, so please correct me where I'm wrong.

Table

MyTable
-------------------------
ID            number (PK)
NEW_FIELD     number
DESCRIPTION   text

Main Entity

@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 */
}

Sub entity

@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);

Exception

Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.

[...]

Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: ERROR: relation "new_field" does not exist

[...]

Caused by: org.hibernate.exception.SQLGrammarException: ERROR: relation "new_field" does not exist

[...]

Caused by: org.postgresql.util.PSQLException: ERROR: relation "new_field" does not exist

What am I doing wrong ? I'm pretty new to JPA 2 and I've never used an entity not associated to a physical table... this approach is totally new to me.

I guess I need to put the @Column definition somewhere: how could JPA possibly know that the newField column (mapped through ImprovedNamingStrategy to new_field on the database) is retrieved through the value property of the FooSequence entity ?

Some pieces of the puzzle are missing.


EDIT

As asked in comments, this is the 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>

解决方案

One possible solution is to use a separate entity with its own table that will encapsulate only the new field and have an OneToOne mapping with that entity. You will then instantiate the new entity only when you encounter an object that needs the additional sequence number. You can then use any generator strategy to populate it.

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

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

See:

A gradle 1.11 runnable SSCCE (using 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();
    }
}

build.gradle

apply plugin: 'java'
defaultTasks 'execute'

repositories {
    mavenCentral()
    maven { url "http://repo.spring.io/libs-milestone" }
}

dependencies {
    compile "org.springframework.boot:spring-boot-starter-data-jpa:1.0.0.RC5"
    compile "org.projectlombok:lombok:1.12.6"
    compile "com.h2database:h2:1.3.175"
}

task execute(type:JavaExec) {
    main = "JpaMultikeyDemo"
    classpath = sourceSets.main.runtimeClasspath
}

See also: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-configure-datasource

这篇关于JPA - 以编程方式通过序列增加数字字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆