JPA或Hibernate生成一个(非主键)列值,不是从1开始
JPA or Hibernate to generate a (non primary key) column value, not starting from 1
我想要一个可以生成列值的 JPA/Hibernate(最好是 JPA)注释,它不是主键并且不是从 1 开始的。
据我所知,JPA 无法使用@GeneratedValue 和@SequenceGenerator 以及@TableGenerator 做到这一点。或其他任何东西。
我看到一个 solution 有一个额外的 table,我觉得不优雅。
我可以接受 Hibernate 注释,因为我已经有了 hibernate 注释。
我想使用@Generated,但我无法让它工作,人们claim认为这是可能的。
@Generated(GenerationTime.INSERT)
private long invoiceNumber;//invoice number
更新:一个额外的要求,如果事务被回滚,我们在编号上不能有空隙。
有人吗?
@GeneratedValue
仅适用于标识符,因此您不能使用它。如果您使用 MySQL,您将受到很大限制,因为不支持数据库序列。
InnoDB doesn't support multiple AUTO_INCREMENT columns 如果您的 table PK 是 AUTO_INCREMENTED,那么您有两个选择:
寻求一个单独的 table,其行为类似于序列生成器,您已经说过您不满意的解决方案。
使用 INSERT TRIGGER 增加该列。
以下是对我有用的方法 - 我们在服务中对所有内容进行了编码。
这是实体:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Registrant extends AbstractEntity {
//....
private long invoiceNumber;//invoice number
@Entity
public static class InvoiceNumberGenerator {
@Id
@GeneratedValue
private int id;
private long counter;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public long getCounter() {
return counter;
}
public void setCounter(long counter) {
this.counter = counter;
}
}
}
然后我们有一个神奇的服务(实际上没有魔法,都是手动完成的):
public synchronized Registrant save(Registrant registrant) {
long counter = getInvoiceNumber();
registrant.setInvoiceNumber(counter);
return registrantRepository.save(registrant);
}
private long getInvoiceNumber() {
//mist: get the invoice number from the other table
long count = registrantInvoiceNumberGeneratorRepository.count();
if(count > 1) {
throw new RuntimeException(": InvoiceNumberGenerator table has more than one row. Fix that");
}
Registrant.InvoiceNumberGenerator generator;
if(count == 0) {
generator = new Registrant.InvoiceNumberGenerator();
generator.setCounter(1000001);
generator = registrantInvoiceNumberGeneratorRepository.save(generator);
} else {
generator = registrantInvoiceNumberGeneratorRepository.findFirstByOrderByIdAsc();
}
long counter = generator.getCounter();
generator.setCounter(counter+1);
registrantInvoiceNumberGeneratorRepository.save(generator);
return counter;
}
注意 synchronized
方法 - 这样就没有人可以得到相同的号码。
我不敢相信没有什么可以自动做到这一点。
与@Vlad Mihalcea 相关,现在您可以使用@GeneratorType 为非 id 列生成您自己的自定义值。例如:
- 实体:
import org.hibernate.annotations.GeneratorType
@GeneratorType(type = CustomGenerator.class, when = GenerationTime.INSERT)
@Column(name = "CUSTOM_COLUMN", unique = true, nullable = false, updatable = false, lenght = 64)
private String custom;
- ValueGenerator 实现:
public class CustomGenerator extends ValueGenerator<String> {
private static final String TODAY_EXAMPLE_QUERY = "from Example where createDate>:start and createDate<:end order by createDate desc";
private static final String START_PARAMETER = "start";
private static final String END_PARAMETER = "end";
private static final String NEXTVAL_QUERY = "select EXAMPLE_SEQ.nextval from dual";
private final SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMdd");
@Override
public String generateValue(Session session, Object owner) {
Date now = new Date();
Query<Example> todayQuery = session.createQuery(TODAY_EXAMPLE_QUERY, Example.class);
query.setParameter(START_PARAMETER, start(now));
query.setParameter(END_PARAMETER, end(now));
Example lastExample = todayQuery.setMaxResult(1).setHibernateFlushMode(COMMIT).uniqueResult();
NativeQuery nextvalQuery = session.createSQLQuery(NEXTVAL_QUERY);
Number nextvalValue = nextvalQuery.setFlushMode(COMMIT).uniqueResult();
return dataFormat.format(now) + someParameter(lastExample) + nextvalValue.longValue();
}
}
我想要一个可以生成列值的 JPA/Hibernate(最好是 JPA)注释,它不是主键并且不是从 1 开始的。
据我所知,JPA 无法使用@GeneratedValue 和@SequenceGenerator 以及@TableGenerator 做到这一点。或其他任何东西。
我看到一个 solution 有一个额外的 table,我觉得不优雅。
我可以接受 Hibernate 注释,因为我已经有了 hibernate 注释。
我想使用@Generated,但我无法让它工作,人们claim认为这是可能的。
@Generated(GenerationTime.INSERT)
private long invoiceNumber;//invoice number
更新:一个额外的要求,如果事务被回滚,我们在编号上不能有空隙。 有人吗?
@GeneratedValue
仅适用于标识符,因此您不能使用它。如果您使用 MySQL,您将受到很大限制,因为不支持数据库序列。
InnoDB doesn't support multiple AUTO_INCREMENT columns 如果您的 table PK 是 AUTO_INCREMENTED,那么您有两个选择:
寻求一个单独的 table,其行为类似于序列生成器,您已经说过您不满意的解决方案。
使用 INSERT TRIGGER 增加该列。
以下是对我有用的方法 - 我们在服务中对所有内容进行了编码。 这是实体:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Registrant extends AbstractEntity {
//....
private long invoiceNumber;//invoice number
@Entity
public static class InvoiceNumberGenerator {
@Id
@GeneratedValue
private int id;
private long counter;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public long getCounter() {
return counter;
}
public void setCounter(long counter) {
this.counter = counter;
}
}
}
然后我们有一个神奇的服务(实际上没有魔法,都是手动完成的):
public synchronized Registrant save(Registrant registrant) {
long counter = getInvoiceNumber();
registrant.setInvoiceNumber(counter);
return registrantRepository.save(registrant);
}
private long getInvoiceNumber() {
//mist: get the invoice number from the other table
long count = registrantInvoiceNumberGeneratorRepository.count();
if(count > 1) {
throw new RuntimeException(": InvoiceNumberGenerator table has more than one row. Fix that");
}
Registrant.InvoiceNumberGenerator generator;
if(count == 0) {
generator = new Registrant.InvoiceNumberGenerator();
generator.setCounter(1000001);
generator = registrantInvoiceNumberGeneratorRepository.save(generator);
} else {
generator = registrantInvoiceNumberGeneratorRepository.findFirstByOrderByIdAsc();
}
long counter = generator.getCounter();
generator.setCounter(counter+1);
registrantInvoiceNumberGeneratorRepository.save(generator);
return counter;
}
注意 synchronized
方法 - 这样就没有人可以得到相同的号码。
我不敢相信没有什么可以自动做到这一点。
与@Vlad Mihalcea 相关,现在您可以使用@GeneratorType 为非 id 列生成您自己的自定义值。例如:
- 实体:
import org.hibernate.annotations.GeneratorType
@GeneratorType(type = CustomGenerator.class, when = GenerationTime.INSERT)
@Column(name = "CUSTOM_COLUMN", unique = true, nullable = false, updatable = false, lenght = 64)
private String custom;
- ValueGenerator 实现:
public class CustomGenerator extends ValueGenerator<String> {
private static final String TODAY_EXAMPLE_QUERY = "from Example where createDate>:start and createDate<:end order by createDate desc";
private static final String START_PARAMETER = "start";
private static final String END_PARAMETER = "end";
private static final String NEXTVAL_QUERY = "select EXAMPLE_SEQ.nextval from dual";
private final SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMdd");
@Override
public String generateValue(Session session, Object owner) {
Date now = new Date();
Query<Example> todayQuery = session.createQuery(TODAY_EXAMPLE_QUERY, Example.class);
query.setParameter(START_PARAMETER, start(now));
query.setParameter(END_PARAMETER, end(now));
Example lastExample = todayQuery.setMaxResult(1).setHibernateFlushMode(COMMIT).uniqueResult();
NativeQuery nextvalQuery = session.createSQLQuery(NEXTVAL_QUERY);
Number nextvalValue = nextvalQuery.setFlushMode(COMMIT).uniqueResult();
return dataFormat.format(now) + someParameter(lastExample) + nextvalValue.longValue();
}
}