CREATE TABLE `SomeEntity` ( `id` int(11) NOT NULL AUTO_INCREMENT,`subid` int(11) NOT NULL DEFAULT '0',PRIMARY KEY (`id`,`subid`),
我有一个实体类,其中有一个自动增量字段.我想要读取自动递增id,当它被持久化分配给它
吸气剂的注释如下
private long id; private int subid; @Id @GeneratedValue **//How do i correct this to have multiple rows with same id and different subid** @Column(name = "id") public long getId() { return id; } @Id @Column(name = "subid") public int getSubid() { return subid; }
我想要有实体
id 1 subid 0 id 1 subid 1 id 1 subid 2 id 2 subid 0
subid在数据库中是默认值0,我以编程方式增加对该行的更新.
我试过这个SO SO的解决方案
JPA – Returning an auto generated id after persist()
@Transactional @Override public void daoSaveEntity(SomeEntity entity) { entityManager.persist(entity); }
现在在这个事务之外,我试图获得分配的自动递增ID
@Override public long serviceSaveEntity(SomeEntity entity) { dao.daoSaveEntity(entity); return entity.getId(); }
我从网络服务中呼叫这个
@POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Response createEntity(SomeEntity entity) {
更新方法如下
@Transactional public void updateReportJob(SomeEntity someEntity) { Query query = entityManager .createQuery("UPDATE SomeEntity SET state=:newState WHERE id = :id"); query.setParameter("newState","PASSIVE"); query.setParameter("id",id); query.executeUpdate(); double rand = Math.random(); int i = (int) (rand * 300); try { Thread.sleep(i); //only to simulate concurrency issues } catch (InterruptedException e) { e.printStackTrace(); } List<Integer> resList = entityManager.createQuery("select max(subid) from SomeEntity WHERE id = :id") .setParameter("id",jobId).getResultList(); // Increment old subid by 1 int subid = resList.get(0); SomeEntity.setsubid(subid + 1); SomeEntity.setState("ACTIVE"); // entityManager.merge(SomeEntity); entityManager.persist(SomeEntity); }
我从N线程发送N个并发更新,实体为Id 1,其他几个属性如下
SomeEnity entity = new SomeEntity();
entity.setId(1); long num = Thread.currentThread().getId(); entity.setFieldOne("FieldOne" + num); entity.setFieldTwo("" + i); entity.setFieldThree("" + j); i++; j++;
案例1具有@Id和idid上的@Id注释和更新中的“entityManager.persist”
当我运行300线程一些失败与连接异常“太多的连接”数据库状态是
id 1 subid 0 id 1 subid 1 id 1 subid 2 .. .... id 1 subid 150
子系统总是增量,竞争条件只是因为竞争条件而被定义为ACTIVE
案例2带有@Id和idid上的@Id注释和更新中的“entityManager.merge”
id 1 subid 0
id 1 subid 0
id 2 subid 0
.. ….
id 151 subid 0(也许只是一个共同点,一个线程比一个线程成功?)
案例3使用@GeneratedValue和@Id在id和** NO @Id注释在subid和entityManager.persist在更新**
异常 – 分离实体传递到持久化
情况3使用@GeneratedValue和@Id对id和** NO @Id注释在subid和entityManager.merge在更新**
如果更新顺序运行,则数据库状态为
id 1 subid 0
下次更新后
id 1 subid 1
在每次更新之后,相同的行被更新(一次只导致一行)
id 1 subid 2
案例4与案例3相同,并发更新
如果同时运行(有300个线程),我得到以下异常
org.hibernate.HibernateException: More than one row with the given identifier was found: 1
数据库状态是
id 1 subid 2(只有一个线程将成功,但由于竞争条件更新subid从0到2)
案例5使用@GeneratedValue和@Id对idid和@Id注释在subid上
创建也失败与subid org.hibernate.PropertyAccessException:IllegalArgumentException发生时调用setter的SomeEntity.id
请解释原因.从我知道的方法的javadoc
持久化 – 使一个实例被管理和持久化.
merge – 将给定实体的状态合并到当前持久性上下文中.
我的问题更多的是Hibernate如何管理注释.
如果会话未关闭,为什么在情况3中有一个脱离实体异常?
为什么在这种情况下有一个IllegalArgumentException异常?
我正在使用hibernate 3.6 MysqL 5和spring 4
还请建议一种实现这样的增量id和subid的方法(使用自定义SelectGenerator,具有演示实现或任何其他方式,而不做列concat)
解决方法
@Id @Column(name = "id") public long getId() { return id; } @Column(name = "subid") public int getSubid() { return subid; }
实体可以通过id使用实体管理器:
entityManager.find(MyEntity.class,entityId);
MyEntity myEntity = entityManager.createTypeQuery("select me from MyEntity where id = :id and subid = :subid",MyEntity.class) .setParameter("id",entityId) .setParameter("subid",entitySubId) .getSingleResult();
Hibernate还有一个可以从数据库列获取id的SelectGenerator,当数据库使用触发器生成ID时,这是非常有用的.
不幸的是,它不适用于组合ids,所以你会写下你自己的扩展的SelectGenerator或使用单个字符串id_sub_id列,将id和sub-id组合成一个VARCHAR列:
'1-0' '1-1' '2-0' '2-1'
您必须使用数据库特定的存储过程编写数据库触发器来更新两列,并将两列集合到VARCHAR存储过程中.然后,使用标准SelectGenerator将聚合列映射到字符串字段:
@Id @Column(name = "id_sub_id") @GeneratedValue( strategy = "trigger" ) @GenericGenerator( name="trigger",strategy="org.hibernate.id.SelectGenerator",parameters = { @Parameter( name="keys",value="id_sub_id" ) } ) public String getId() { return id; }