JPA - 以编程方式通过序列增加数字字段 [英] JPA - Increment a numeric field through a sequence programmatically
问题描述
我有一个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);
例外 导致:javax.transaction.RollbackException:ARJUNA016053:无法提交事务。 [...] 导致:javax.persistence.PersistenceException:org.hibernate.exception.SQLGrammarException:ERROR:关系new_field不存在 [...] 导致:org.hibernate.exception.SQLGrammarException:错误:关系new_field不存在 引起:org.postgresql.util.PSQLException:错误:关系new_field不存在 我做错了什么?我对JPA 2非常陌生,并且从未使用过与物理表关联的实体......这种方法对我来说是全新的。 我想我需要把 缺少一些难题。 编辑 正如评论中所述,这是 一种可能的解决方案是使用一个单独的实体自己的表,将只封装新的字段,并与该实体具有OneToOne映射。只有在遇到需要附加序列号的对象时,才会实例化新实体。您可以使用任何生成器策略来填充它。 请参阅: Gradle 1.11可运行的SSCCE(使用Spring Boot): build.gradle 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 For example: In the good old SQL, it would be something like: 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 [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. 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 Main Entity Sub entity DAO 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 Some pieces of the puzzle are missing. EDIT As asked in comments, this is the
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. See: A gradle 1.11 runnable SSCCE (using Spring Boot): src/main/java/JpaMultikeyDemo.java build.gradle 这篇关于JPA - 以编程方式通过序列增加数字字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
< blockquote>
$ b
@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/>
< property name =hibernate.show_sqlvalue =true/>
< property name =format_sqlvalue =true/>
< property name =use_sql_commentsvalue =true/>
< / properties>
< / persistence-unit>
< /余辉>
@Entity
公共类FooSequence {
@ Id
@GeneratedValue(...)
私有长值;
}
@Entity
公共类别无论{
@OneToOne(...)
私人FooSequnce newColumn;
li>
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();
}
}
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
}
its highest value + 1
or to 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
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....'
)
- 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.
EDIT
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 */
}
myEntity.setNewField(new FooSequence());
entityManager.persist(myEntity);
@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 ?
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>
@Entity
public class FooSequence {
@Id
@GeneratedValue(...)
private Long value;
}
@Entity
public class Whatever {
@OneToOne(...)
private FooSequnce newColumn;
}
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();
}
}
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
}