views:

48

answers:

1

Hi,

I am learning Hibernate and just read the chapter "7.2.3 Adding columns to join tables" of the "Java Persistance with Hibernate" book. My goal is to save Item, Category and CategorizedItem in one transaction.

There is a constructor there (page 305):

public CategorizedItem(String username, Category category, Item item) {
    // Set fields
    this.username = username;

    this.category = category;
    this.item = item;

    // Set identifier values
    this.id.categoryId = category.getId();
    this.id.itemId = item.getId();

    // Guarantee referential integrity
    category.getCategorizedItems().add(this);
    item.getCategorizedItems().add(this);
}

It accepts category and item objects. If I create a Category and an Item and want to connect them with this technique, they obviously have to be persisted BEFORE, as category.getId() and item.getId() return null.

Is there "a trick in the Hibernate bag" that can cascade the saving of a join table? The join table have additional columns. I want to save all three objects in the onSuccess handler in my web page controller. All three entities or none of them must be inserted.

Thanks.

+1  A: 

You said

My goal is to save Item, Category and CategorizedItem in one transaction

Is there a trick in the Hibernate bag that can cascade the saving of a join table ?

Yes, use MutableInt

@Entity
public class CategorizedItem implements Serializable {

    private CategorizedItemId categorizedItemId;

    private String userName;

    private Category category;
    private Item item;

    /**
      * required no-arg constructor
      */
    public CategorizedItem() {}
    public CategorizedItem(CategorizedItemId categorizedItemId) {
        this.categorizedItemId = categorizedItemId;
    }
    public CategorizedItem(String userName, Category category, Item item) {
        this.userName = userName;

        this.categorizedItemId = new CategorizedItemId(category.getIdAsMutableInt(), item.getIdAsMutableInt());
    }

    @EmbeddedId
    public CategorizedItemId getCategorizedItemId() {
        return this.categorizedItemId;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="CATEGORY_ID", insertable=false, updateable=false)
    public Category getCategory() {
        return this.category;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="ITEM_ID", insertable=false, updateable=false)
    public Item getItem() {
        return this.item;
    }

    // setter's goes here

    /**
      * It MUST implements Serializable
      * It MUST overrides equals and hashCode method
      * It MUST has a no-arg constructor
      *
      * Hibernate/JPA 1.0 does not support automatic generation of compound primary key
      * You SHOULD set up manually
      */
    @Embeddable
    public static class CategorizedItemId implements Serializable {

        private MutableInt categoryId = new MutableInt(-1);
        private MutableInt itemId = new MutableInt(-1);

        /**
          * required no-arg constructor
          */
        public CategorizedItemId() {}
        public CategorizedItemId(MutableInt categoryId, MutableInt itemId) {
            this.categoryId = categoryId;
            this.itemId = itemId;
        }

        @Column(name="CATEGORY_ID", updateable=false, nullable=false)
        public Integer getCategoryId() {
            return this.categoryId.intValue();
        }

        public void setCategoryId(Integer categoryId) {
            return this.categoryId.setValue(categoryId);
        }

        @Column(name="ITEM_ID", updateable=false, nullable=false)
        public Integer getItemId() {
            return this.itemId.intValue();
        }

        public void setItemId(Integer itemId) {
            return this.itemId.setValue(itemId);
        }

        // getter's and setter's

        @Override
        public boolean equals(Object o) {
            if(!(o instanceof CategorizedItemId))
                return null;

            finalCategorizedItemId other = (CategorizedItemId) o;
            return new EqualsBuilder().append(getCategoryId(), other.getCategoryId())
                                      .append(getItemId(), other.getItemId())
                                      .isEquals();
        }

        @Override
        public int hashCode() {
            return new HashCodeBuilder().append(getCategoryId())
                                        .append(getItemId())
                                        .toHashCode();  
        }

    }

}

Here goes Category

@Entity
public class Category implements Serializable {

    public MutableInt id = new MutableInt(-1);

    private List<CategorizedItem> categorizedItemList = new ArrayList<CategorizedItem>();

    @Transient
    public MutableInt getIdAsMutableInt() {
        return this.id;
    }

    @Id
    @GeneratedValue
    public Integer getId() {
        return this.id.intValue();
    }

    public void setId(Integer id) {
        return this.id.setValue(id);
    }

    @OneToMany(mappedBy="category")
    @JoinColumn(name="CATEGORY_ID", insertable=false, updateable=false)
    @Cascade(CascadeType.SAVE_UPDATE)
    public List<CategorizedItem> getCategorizedItemList() {
        return categorizedItemList;
    }

    // setter's

    /**
      * Use this method when you have a saved Item
      */
    public void addCategorizedItem(CategorizedItem categorizedItem) {
        categorizedItem.setCategorizedItemId(new CategorizedItemId(getIdAsMutableInt(), categorizedItem.getItem().getIdAsMutableInt()));
        categorizedItem.setCategory(this);

        getCategorizedItemList().add(categorizedItem);
    }

}

And Item

@Entity
public class Item implements Serializable {

    public MutableInt id = new MutableInt(-1);

    private List<CategorizedItem> categorizedItemList = new ArrayList<CategorizedItem>();

    @Transient
    public MutableInt getIdAsMutableInt() {
        return this.id;
    }

    @Id
    @GeneratedValue
    public Integer getId() {
        return this.id.intValue();
    }

    public void setId(Integer id) {
        return this.id.setValue(id);
    }

    @OneToMany(mappedBy="item")
    @JoinColumn(name="ITEM_ID", insertable=false, updateable=false)
    @Cascade(CascadeType.SAVE_UPDATE)
    public List<CategorizedItem> getCategorizedItemList() {
        return this.categorizedItemList;
    }

    // setter's

    /**
      * Use this method when you have a saved Category
      */
    public void addCategorizedItem(CategorizedItem categorizedItem) {
        categorizedItem.setCategorizedItemId(new CategorizedItemId(getIdAsMutableInt(), categorizedItem.getCategory().getIdAsMutableInt()));
        categorizedItem.setItem(this);

        getCategorizedItemList().add(categorizedItem);
    }

}

Now because you need categoryId and itemId before saving CategorizedItem, do as follows

Category category = new new Category();
Item item = new Item();

session.save(new Category());
session.save(new Item());
session.save(new CategorizedItem(userName, category, item));

Notice the cascading just works when you have either a saved Category or a saved Item. Otherwise, you need to follow the approach shown above

Arthur Ronald F D Garcia
+1 Interesting trick with the MutableInt
Pascal Thivent