Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate, automatically persist dependant objects

I'm quite new to Hibernate and have been trying to determine what it will do for you and what it requires you to do.

A big one is dealing with an object that has dependants that don't yet exist in the database. For example, I have a Project object that includes a Manufacturer field that accepts a Manufacturer object as its value. In the database I have a products table with a mfr_id column that's a reference to the manufacturers table (a fairly typical unidirectional one-to-many relationship).

If the manufacturer assigned to the product object relates to one that's already in the database then there's no problem. However, when I try to save or update an object that references a manufacturer that hasn't been persisted yet, the operation fails with an exception.

Exception in thread "Application" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing

I can of course manually check the state of the product's manufacturer by seeing if it's ID field is null and saving it if it is, but this seems like a cumbersome solution. Does Hibernate support automatically persisting dependants if the dependant in question isn't yet persisted? If so, how do I enable that behaviour? I'm using the version of Hibernate bundled with Netbeans (3.5, I believe) and inline annotations for specifying the mapping behaviour. Below are my product and manufacturer classes, cut down to the parts that handle the dependency. (Product extends Sellable which maps to a sellable table, using JOINED as the inheritance strategy It's that table that contains the primary key that identifies the product)

@Entity
@Table (
        name="products",
        schema="sellable"
)
public abstract class Product extends Sellable {
    private Manufacturer                        manufacturer;

    @ManyToOne (fetch = FetchType.EAGER)
    @JoinColumn (name = "mfr_id")
    public Manufacturer getManufacturer () {
        return this.manufacturer;
    }

    /**
     * 
     * @param manufacturer 
     */
    public Product setManufacturer (Manufacturer manufacturer) {
        this.manufacturer   = manufacturer;
        return this;
    }
}

The dependant Manufacturer

@Entity
@Table (
        name="manufacturers",
        schema="sellable",
        uniqueConstraints = @UniqueConstraint(columnNames="mfr_name") 
)
public class Manufacturer implements Serializable {
    private Integer         mfrId       = null;
    private String          mfrName     = null;

    @Id
    @SequenceGenerator (name = "manufacturers_mfr_id_seq", sequenceName = "sellable.manufacturers_mfr_id_seq", allocationSize = 1)
    @GeneratedValue (strategy = GenerationType.SEQUENCE, generator = "manufacturers_mfr_id_seq")
    @Column (name="mfr_id", unique=true, nullable=false)
    public Integer getMfrId () {
        return mfrId;
    }

    private Manufacturer setMfrId (Integer mfrId) {
        this.mfrId  = mfrId;
        return this;
    }

    @Column(name="mfr_name", unique=true, nullable=false, length=127)
    public String getMfrName () {
        return mfrName;
    }

    public Manufacturer setMfrName (String mfrName) {
        this.mfrName = mfrName;
        return this;
    }
}

UPDATE: I tried the following from this question, but I still get the transient object exception.

@ManyToOne (fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})

I also checked what version of Hibernate is bundled with Netbeans, it's 3.2.5

UPDATE 2: I found that the following appears to apparently work as I wanted.

@ManyToOne (fetch = FetchType.EAGER, cascade = CascadeType.ALL)

However, I suspect that this is not the cascade type I really want. If I delete a product, I don't think deleting its associated manufacturer is the correct action, which is what I believe will happen now.

I did try creating a cascade type that consisted of all the types that were available, but that didn't work either. I got the same exception when I tried to save a product that had an unsaved manufacturer associated with it.

@ManyToOne (fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})

I've seen CascadeType.SAVE_UPDATE mentioned in several places, but that mode doesn't seem to be available in the version of Hibernate that comes with Netbeans.

like image 537
GordonM Avatar asked Aug 26 '13 22:08

GordonM


1 Answers

You have to look at cascading operations; this type of operation permits you to manage lifecycle of inner object respect their parent.

@ManyToOne(cascade) if you use Session.persist() operation or org.hibernate.annotations.@Cascade if you use not JPA function Session.saveOrUpdate().

This is just an example, for full doc point here

For your code, if you want to automatically save Manufacturer when saving Project use:

@ManyToOne (fetch = FetchType.EAGER, cascade = {javax.persistence.CascadeType.PERSIST})
@JoinColumn (name = "mfr_id")
public Manufacturer getManufacturer () {
  return this.manufacturer;
}

or

@Cascade(CascadeType.PERSIST)
like image 139
Luca Basso Ricci Avatar answered Nov 01 '22 16:11

Luca Basso Ricci