Hibernate, automatically persist dependant objects

17,894

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)
Share:
17,894

Related videos on Youtube

GordonM
Author by

GordonM

I'm a professional PHP programmer with almost a decade of professional experience and considerably more as a hobbyist programmer. I've also had some exposure to Java, Object Pascal and am interested in learning objective C I've started work on a PHP framework, though it's still at a very early stage and not yet really useful. The current source is available on GitHub. I've also begun development of a CSS3 elastic grid for use on my own projects and had published that on GitHub as well.

Updated on June 04, 2022

Comments

  • GordonM
    GordonM over 1 year

    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.

  • GordonM
    GordonM about 10 years
    I tried your suggestion, but I still get the same exception about transient objects when I try to save the object. I also tried the approach suggested in stackoverflow.com/questions/9032998/… of @ManyToOne (fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) but that didn't work either
  • Luca Basso Ricci
    Luca Basso Ricci about 10 years
    are you using saveOrUpdate()? in this case you need org.hibernate.annotation.@Cascade(CascadeType.SAVE_UPDATE)
  • GordonM
    GordonM about 10 years
    At the moment I'm just using save(). I'll look at other options when I get home tonight.
  • GordonM
    GordonM about 10 years
    SAVE_UPDATE doesn't seem to be a valid cascade type in the version of Hibernate that came with Netbeans. The hint lists the available options as ALL, PERSIST, MERGE, REFRESH or REMOVE
  • Luca Basso Ricci
    Luca Basso Ricci about 10 years
    You are using javax.persistence.CascadeType; you have to use org.hibernate.annotation.CascadeType with org.hibernate.annotation.@Cascade annotation
  • GordonM
    GordonM about 10 years
    Actually it's annotations, plural. :) Giving it a try now.
  • GordonM
    GordonM about 10 years
    The @cascade method works as I was expecting. I guess I was just having some confusion regarding persistence annotations versus hibernates annotations. I'm still pretty new to Hibernate and it's taking a while to figure it out. Thanks for the help
  • Luca Basso Ricci
    Luca Basso Ricci about 10 years
    Is preferable to use standard annotations and use Hibernate' specifics only when standard does not cover your requirements