Home > Enterprise >  what is the reason that Hibernate does not allow to save the object which references an unsaved tran
what is the reason that Hibernate does not allow to save the object which references an unsaved tran

Time:04-30

I'm newbie in Hibernate and I'm trying to learn about JPA and Hibernate.

I want to know that what is the reason that Hibernate does not allow to save the object which references an unsaved transient instance? I want to know WHY this is a problem?

I asked someone and some of them answer me like this:

How could we possibly map the customer to the address, if there is no adress record in the DB yet?

and

you are assigning particular Address to Customer. But Address does not have any ID

but honestly I can't understand them.

(I know that an exception will be thrown and the solution is Cascade but I want to the reason of the problem inside the database)

now, let's assume we have all of these code:

(I use Bidirectional One-To-One relationship for my example)

public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String firstName;
    private String lastName;


    @OneToOne(mappedBy = "customer")
    private Address address;
}


@Entity
public class Address { 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String street;
    private String zipCode;

    @OneToOne
    private Customer customer;
}

   public static void main(String[] args) {

        EntityManager entityManager = emf.createEntityManager();
        entityManager.getTransaction().begin(); // Begin Transaction

        Customer c1 = new Customer("Mi", "S");
        Address addrss1 = new Address("5412 S 5th", "212524");

        c1.setAddress(addrss1);
        addrss1.setCustomer(c1);


        entityManager.persist(c1);

        entityManager.getTransaction().commit(); // Commit 
        entityManager.close();
    }

and let's assume that the exception is not thrown and java and hibernate have allowed us to run our code and this is our customer table.

   id    firstName    lastName    
---------------------------------
    1        Mi           S

now, what is the problem? everything in these Bidirectional One-To-One relationship seems right. then what is the problem?


PS: if it is possible, please explain and show me code. I can understand better with code. thank you.

I want to see for example if we are allowed to save the object which references an unsaved transient instance, what problems will we face in our code and in our tables (for example do we have any problem when we want to retrieve a customer and etc)

CodePudding user response:

Customer is new, and it is clear from the persist call you want to insert it, but it isn't clear what you want to happen to any of customer's references. To make it clear, you define what you want the JPA provider (Hibernate) to do in the mappings under any/all circumstances - this is what the cascade operations refer to. In this case, JPA will look at the customer.address OneToOne mapping and find nothing defined; Address is NOT managed in this EntityManager context, so it doesn't know what to do to handle this relationship, so it signals you've made a mistake by throwing an error.

If it let it through, your Customer instance references something that does not exist, and its state does not match what is in the database. What you pass into persist should be what you would get back on reads, so it should reflect the state that is in the database.

The issue isn't directly with your persist call, as the spec does allow providers to ignore references to detached/new instances that don't have cascade settings - what happens is just undefined. Where you go wrong in this situation is on flush/commit, which is when the persistence unit is synchronized to the database (section 3.2.4 of JPA 3.0), which requires providers to go through managed entities and then determine any changes. Adding a new address pre persist will result in the same issue as if you did it post persist, and requires providers to throw an IllegalStateException if it discovers new or removed entities and rollback the transaction.

Why this is a problem: JPA is very big on entity Identity, as this enables caching of these entities in multiple levels of caches, and this entity might go into those caches as it is. It has to know what to do with references to entities that do not exist, and the spec decided to require an exception. Even to your app this is and should be a problem, as the EntityManager context is a unit of work, and the state within that unit of work is based on something that is wrong. Your Customer doesn't really have an address when this is said and done, yet your application business logic thinks it assigned one, with state that just isn't going to be there afterward.

You already know the solutions:

  • correct the customer to have a valid, managed address by calling persist on it directly in this same EntityManager context.
  • set the cascade options on the mapping to cascade persist to address for you
  • don't set addresses on a new customer in the same operation.

CodePudding user response:

Because your adress entity have the primay key of customer as a foreign key ,(since mappedby is in Customer entity) ,and the customer referenced by the adress has no id ,which tells hibernate that that entity was never persisted in the database (which literally means transient) ,and hibernate needs a persisted/managed entity to make sure it exists in the database so that the adress object can be associated with an existing customer.

  • Related