繁体   English   中英

Session.flush()无法按预期工作

[英]Session.flush() not working as expected

在服务类中,我有一个从@Transactional方法调用的方法。 我已经验证在调用此代码时我的事务处于活动状态。 我意识到我应该没有DA层,但我正在使用遗留应用程序,这使得“正确”的做法更加麻烦,而不是在这一点上。

映射看起来像这样:

public class Foo {

  private String id;
  private Bar bar;

  @Id
  @Column(name = "FOO_ID", unique = true, nullable = false, length = 16)
  @GeneratedValue(generator = "blahIdSeq")
  @GenericGenerator(name = "blahIdSeq",
                    strategy = "org.blah.CustomIdGenerator")
  public String getId() {return id;}

  @JoinColumn(name = "FOO_ID")
  @OneToOne(fetch = FetchType.LAZY, optional = false)
  public Bar getBar() { return bar; }

  // SETTERS INCLUDED

}

public class Bar {
  private String id;
  private Foo foo;

  @Id
  @Column(name = "FOO_ID")
  @GeneratedValue(generator = "someSeq")
  @GenericGenerator(name = "someSeq",
                    strategy = "foreign",
                    parameters = {
                      @Parameter(name = "property", value = "foo")
                    })
  public String getId() { return id; }

  @OneToOne(fetch = FetchType.LAZY)
  @PrimaryKeyJoinColumn(name = "FOO_ID")
  public Foo getFoo() { return foo; }

  // SETTERS INCLUDED

}

该方法看起来像这样:

public String createFoo(Foo foo) {
  Session ses = getSessionFactory().getCurrentSession();
  Bar bar = new Bar();
  bar.setFoo(foo);
  foo.setBar(bar);
  ses.save(foo);
  ses.save(bar);

  System.out.println(foo.getId()); // yields the ID value set by generator
  System.out.println(bar.getId()); // yields same ID value as above

  ses.flush();
  ses.refresh(foo);
}

现在,将org.hibernate.SQL日志记录设置为DEBUG ,我可以看到创建了Foo和Bar的insert语句,但刷新后刷新被抛出org.hibernate.UnresolvableObjectException: No row with the given identifier exists例外。

什么可能导致这个? 使用的数据库是Oracle 11gR2。

UPDATE

我已将问题缩小到会议。 似乎调用currentSession.flush()不是按照预期的那样将数据写入数据库进行刷新。 如果我注释掉方法的其余部分,它将在最后提交,一切都将在数据库中。

但是,执行刷新/刷新不会返回水合对象,因此我不能在事务中使用数据库填充的值(由列默认值设置)。 我也无法将事务拆分为多个事务,因为我需要能够在方法中的任何位置回滚。

关于为什么flush不给我数据库中的可访问数据的任何想法?

另一个更新

我已经移动了很多代码只是为了尝试找出问题,我仍然遇到问题。 我也摆脱了两个实体之间的关系,试图手动处理所有事情,只是为了看看是否能解决问题。 考虑到史蒂夫的所有评论,这就是我现在所拥有的:

public class Foo {

  private String id;
  private Bar bar;

  @Id
  @Column(name = "FOO_ID", unique = true, nullable = false, length = 16)
  @GeneratedValue(generator = "blahIdSeq")
  @GenericGenerator(name = "blahIdSeq",
                    strategy = "org.blah.CustomIdGenerator")
  public String getId() {return id;}

  // SETTERS INCLUDED

}

public class Bar {
  private String id;
  private Foo foo;

  @Id
  @Column(name = "FOO_ID")
  public String getId() { return id; }

  // SETTERS INCLUDED

}

@Service('fooService')
@Transactional(readOnly = true)
class FooService {
  @Autowired
  SessionFactory sessionFactory // populated using Spring config:
                                // org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean

  @Transactional(readOnly = false)
  public void doSomeStuff(Foo fooToSave) {
    sessionFactory.getCurrentSession().saveOrUpdate(fooToSave);
    Bar bar = new Bar(fooToSave); // this populates the Bar.Id field
    sessionFactory.getCurrentSession().saveOrUpdate(bar);
    sessionFactory.getCurrentSession().flush();
    sessionFactory.getCurrentSession().refresh(fooToSave); // exception thrown here
  }
}

再来一次更新

在Oracle-land中进行了相当多的游戏以确保SQL在同一个会话中运行之后,我发现了这个问题。 即使Hibernate正在记录正在设置SQL绑定变量,但它们实际上并非如此。 使用Oracle 11gR2的V$SQL_BIND_CAPTURE ,我能够看到使用最后执行的SQL ID(验证为insert语句)有24个绑定变量,而其中没有一个绑定了它的值。 仍然不确定是什么导致价值空白,但我更接近找到我的答案。 它必须是我的映射的问题,我不能完全放在这里。

作为绑定变量,我猜测Oracle不会因为无法插入而变得合适。 JDBC通常只返回为INSERT语句插入的行数以进行验证,但我不确定Hibernate抽象是如何处理这些东西的。 我目前正在使用Hibernate 3.6.10 - 从3.6.5升级,看它是否可以解决问题。 它没有。 :P

我已经误入歧途了

忽略上面的“YET ANOTHER UPDATE”部分。 绑定变量似乎在提交事务之前不会显示在V$SQL_BIND_CAPTURE视图中。 回到绘图板。

另一个修改 - 我很惊讶我被GONNA禁止了

我决定回到基础。 自从它处于工作状态以来我有什么变化? 主要是映射。 一些服务层项也发生了变化,但它主要是将我们的Hibernate映射从XML转移到注释。 所以我采用了我一直在使用的相同服务方法,注释掉了所有其他的东西,并尝试做与我正在尝试使用另一个持久对象类型的Foo完全相同的事情。 你猜怎么了? 这样可行。 在这一点上唯一可能引起我心痛的环节是我对Foo的映射。 我怀疑我的雇主是否希望我在SO上完全放弃,所以我可能不得不自己想出这个。 当我最终弄明白时,我会以某种身份发布答案。

成功! 但我不确定为什么......

这是给我带来麻烦的代码。 请记住, BAZ是有一个由复合ID的链接表@Embeddable (只是所谓的“钥匙”在这个例子中),由FOO_ID引用了一排FOO表和STATE_ID引用另一个表。

public class Foo {

  // OTHER FIELDS INCLUDING IDs AND SUCH

  private Baz bazOfDoom;
  private Baz bazOfLight;
  private Set<Baz> allTheBaz;

  @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
  @JoinColumns({
    @JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID", insertable = false, updatable = false, nullable = false)
    @JoinColumn(name = "DOOM_ID", referencedColumnName = "STATE_ID", insertable = false, updatable = false, nullable = false)
  })
  public Baz getBazOfDoom() { return bazOfDoom; }

  @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
  @JoinColumns({
    @JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID", insertable = false, updatable = false, nullable = false)
    @JoinColumn(name = "LIGHT_ID", referencedColumnName = "STATE_ID", insertable = false, updatable = false, nullable = false)
  })
  public Baz getBazOfLight() { return bazOfLight; }

  @OneToMany(mappedBy = "key.foo", fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
  public Set<Baz> getAllTheBaz() { return allTheBaz; }
}

我删除了级联,它工作。 我不知道为什么。 无论谁能解释,都会得到我的“正确答案”标记。 :)

在将对象保存到数据库之后,您的对象似乎没有对象的标识,因此在调用refresh()时会导致异常。

实际上,假设您的数据库表拥有定义为auto-increment的主键。因此,当您保存第一个Foo对象时,主键列的值为: 1

但是,在调用save()方法之后,Hibernate必须知道这个新生成的标识符!

执行此操作的最佳方法是,只要将对象保存到数据库中,就会期望Hibernate重新生成良好的标识符。

因此,您可能会错过实体类中的这一行,以便在将对象保存在数据库中时自动提供标识符:

@GeneratedValue(strategy = GenerationType.AUTO)

当然,您不必自动生成它们,而是可以手动精确它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM