Java 休眠一对一映射。仅插入外键,而不是整个新行

Java 休眠一对一映射。仅插入外键,而不是整个新行,java,oracle,hibernate,Java,Oracle,Hibernate,我有RECEIPE table和Cookie_ID是Cookie.ID table的外键。插入新菜谱时可以,只插入烹饪ID,烹饪表中没有新行 表结构 create table CUISINE(cuisine_id number primary key, name varchar2(20)); create table RECIPE(id number primary key, name varchar2(25) not null,

我有RECEIPE table和Cookie_ID是Cookie.ID table的外键。插入新菜谱时可以,只插入烹饪ID,烹饪表中没有新行

表结构

create table CUISINE(cuisine_id number primary key, name varchar2(20));

create table RECIPE(id number primary key,
                    name varchar2(25) not null,
                    cuisine_id number,
                    constraint cuisine_fk foreign key (cuisine_id) references
                    cuisine(cuisine_id));
类映射:

@Entity
@Table(name = "cuisine")
public class Cuisine {
    @Id
    @Column(name = "cuisine_id")
    private int id;

    @Column(name = "name")
    @NotBlank(message = "You can not leave this empty.")
    @NotNull(message = "You can not leave this empty.")
    private String name;


@Entity
@Table(name = "recipe")
public class Recipe {
    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "name")
    private String name;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "cuisine_id")
    private Cuisine cuisine;
一旦我从JSP页面中获得了正在填充的RECEIPE类,我就会从modelAttribute中获得以下内容

Recipe [id=0, name=Burger, cuisine=Cuisine [id=0, name=American]
DAO代码是

public void addRecipe(Recipe recipe) {
        String hql = "select max(id) from Recipe";
        Integer id = ((Integer) getSession().createQuery(hql).uniqueResult()) + 1;

        recipe.setId(id);
        getSession().save(recipe);
    }
从日志中,我可以看到插入了以下内容:

Hibernate: insert into cuisine (name, cuisine_id) values (?, ?)
Hibernate: insert into recipe (cuisine_id, name, id) values (?, ?, ?)
我的要求是烹饪表中没有新的一行作为菜谱插入。cusinie_id应该只存储id


提前感谢,

传递给DAO的Recipe对象可能包含对Cuisine对象的引用。我假设这两个对象当时都没有与会话关联

您对美食的引用的注释包含一个
@OneToOne(cascade=CascadeType.ALL)
,告诉Hibernate也保存引用对象。这将导致您的
插入到
语句中

您可能应该删除级联注释,因为它也会导致在您删除菜谱时删除烹饪


另一种解决方案是从DB加载烹饪对象,并在保存之前在菜谱中引用它。(但这仍然需要删除级联)

因为您是
@OneToOne(cascade=CascadeType.ALL)
我们的代码告诉您,首先必须保存引用,然后保存实际的表值

我在这里看到,您正在一次性保存整个交易

这段代码在DAO实现中可能会有所帮助

public void addRecipe(Recipe recipe) {
    String hql = "select max(id) from Recipe";

    getSession().beginTransaction(); 
    Integer id = ((Integer) getSession().createQuery(hql).uniqueResult()) + 1;

    recipe.setId(id);

    getSession().save(recipe.getCuisine);
    getSession().save(recipe);
    getSession().getTransaction().commmit();

}

向DAO方法传递的只是
Recipe
对象的一个新实例,该对象具有问题中提到的参数:

Recipe [id=0, name=Burger, cuisine=Cuisine [id=0, name=American]
现在,在DAO方法中,您将从数据库获取最大id,并使用新的id值更新您的
配方
实例,因此配方实例的属性将如下所示(假设数据库中的最大id为100):

您的
配方
实体和
烹饪
实体之间的
级联类型
设置为
全部
,因此当您尝试使用以下行保存
配方
实例时:

 getSession().save(recipe);
hibernate将要做的是,它将尝试点击数据库,以获得一个
烹饪
的实例,该实例的
id
0
,因为您的
食谱
有一个
烹饪
的实例,其
id
0

select cuisine_.cuisine_id, cuisine_.name as name2_0_ 
    from cuisine cuisine_ 
    where
        cuisine_.cuisine_id=0
如果数据库中没有id为
0
courine
表的记录,则hibernate假定您正在尝试在数据库中保存
courine
的新记录,因此它也将为
courine
生成
insert
查询


为了避免在
courine
表上执行此
insert
查询,请确保在调用DAO方法之前,将分配给
recipe
对象的
id
实例分配给
courine
对象。

我没有理解您所说的“两个对象当时都与会话不关联”的意思。由于Recipe类具有声明“private Cuisine”,因此在对“Recipe”类进行查询时,这两个对象都将被选中。当您想要存储一个新的配方(即您不只是从数据库中读取的配方)时,可以调用addRecipe()。在session.save(recipe)之前,recipe只是一个数据对象,会话对此一无所知。这就是我所说的“与会议无关”的意思。食谱还引用了一个烹饪对象。我假设这个对象也不是从数据库读取的,会话也不知道关于这个对象的任何信息。级联注释将导致hibernate也保存该对象。谢谢您的建议。删除OneTONE注释中的级联,使代码可执行。但我遇到了另一个与验证相关的例外情况。。我还在琢磨如何解决这个问题。你的代码显式地做了他的代码隐式地做的事情:存储一个对象。这正是他想要避免的。在DAO方法中进行显式事务处理也不是一个好主意,由于该方法可能是更大事务的一部分。我的坏消息是,由于引用表不影响显式代码,因此我认为可能会有帮助,对于事务,我猜测数据不会保存,因为会话可能需要更多操作,这就是我添加transaction()的原因。commit().没有在美食中插入新的一行,您可以在哪里获得美食id?您先插入烹饪吗?抱歉,我尝试过,但代码无法执行。它在courine.Name处给出了验证错误。(使用多个注释来明确我的注释)我保留了@OneToOne(cascade=CascadeType.ALL)。正如您建议的那样,Hibernate在烹饪上运行select查询。但在日志中,在运行insert语句后,它给出了“javax.validation.ConstraintViolationException:在组[javax.validation.groups.Default]的更新期间类[com.recipe.tables.courity]的验证失败,]约束冲突列表:[”@Sandeep,不清楚您尝试了什么,以及您现在遇到了什么异常。请使用您尝试过的确切代码更新您的问题,并完成您现在遇到的异常的stacktrace。
select cuisine_.cuisine_id, cuisine_.name as name2_0_ 
    from cuisine cuisine_ 
    where
        cuisine_.cuisine_id=0