-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: OneToMany bidirectional: saving new objects together
PostPosted: Mon Dec 24, 2007 1:14 pm 
Newbie

Joined: Mon Dec 24, 2007 11:03 am
Posts: 3
Hibernate version: 3.2.5.ga

Hibernate Annotations version: 3.3.0.ga

MySql 5 / PostgreSQL 8.1

Many times I tried using Hibernate for my small projects, but I always gave up at the first collection mapping problem.
Now I can afford some debug time, so I put together a small example demonstrating my newbie problems.

I would like to express a OneToMany bidirectional (or even unidirectional) association with a Map<>, and to save new objects to the database with matching IDs. My example is a User entity with multiple Address related entities. The Map's key is an AddressType column.
What I'd like to obtain is to be able to create a new user, with one or more addresses, and to save it with as little iterations as possible with the back-end.

I tried all the permutations I could imagine, and neither worked, so as a last resort I thought I'd ask for some help here.

Can someone point me in the right direction?

Thanks,
Angelo.

This is the main code (all the juicy part is in the doWork() function):

Code:
package test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;

public class Test {

   /**
    * @param s the Hibernate session to work on
    */
   private static void doWork(Session s) {
      User user = new User("John","blah");
      Address addr = new Address();
      addr.setStreetAddr("Prova Indirizzo");
      user.setAddr(addr, 0);
      
      s.save(user);
      System.out.println("saved "+user.getId());
      s.save(addr);
   }

   
   /**
    * @param args ignored
    */
   public static void main(String[] args) {
      //boilerplate connection setup
      AnnotationConfiguration config = new AnnotationConfiguration();
      config.addAnnotatedClass(User.class)
            .addAnnotatedClass(Address.class);
      config.setProperty("hibernate.hbm2ddl.auto", "update");
      SessionFactory sessionFactory = config.buildSessionFactory();
      Session s = sessionFactory.openSession();
      
        Transaction tx = s.beginTransaction();
       
        doWork(s);

      tx.commit();
      s.close();
        s = null;
   }
}

and the properties file:
Code:
######################
### Query Language ###
######################

## define query language constants / function names

hibernate.query.substitutions true 1, false 0, yes 'Y', no 'N'


## select the classic query parser

#hibernate.query.factory_class org.hibernate.hql.classic.ClassicQueryTranslatorFactory


## MySQL

hibernate.dialect org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class com.mysql.jdbc.Driver
hibernate.connection.url jdbc:mysql://localhost/prove
hibernate.connection.username prove
hibernate.connection.password prove


#################################
### Hibernate Connection Pool ###
#################################

hibernate.connection.pool_size 1

hibernate.transaction.factory_class org.hibernate.transaction.JDBCTransactionFactory

## print all generated SQL to the console

hibernate.show_sql true

hibernate.connection.isolation 8
hibernate.jdbc.batch_versioned_data true

hibernate.jdbc.use_streams_for_binary true

Here are the entities:
Code:
package test;

import java.util.HashMap;
import java.util.Map;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;

@Entity
public class User {
   
    Long id;
    String name;
    String username;
   
    @OneToMany(mappedBy="userID",cascade=CascadeType.ALL)
    @MapKey(name="addrType")
    @JoinColumn(name="id",referencedColumnName="userId")
    public Map<Integer,Address> address;
   
    public User(){}
    public User(String name, String username){
        super();
        this.name = name;
        this.username = username;
    }
   
    @Id @GeneratedValue
    public Long getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public String getUsername() {
        return username;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setUsername(String username) {
        this.username = username;
    }
   public void setAddr(Address addr, int addrType) {
      if (address==null) address = new HashMap<Integer, Address>();

      addr.setAddrType(addrType);
      addr.user = this;
      address.put(addrType,addr);
      
   }
   
   
}

Code:
package test;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Entity
public class Address {

   Long userID;
   Long myId;
   Integer addrType;
   String streetAddr;
   
   @ManyToOne(cascade=CascadeType.ALL)
   @JoinColumn(name="userID",referencedColumnName="id", nullable=false, updatable=false)
   public User user;
   
   public Long getUserID() {
      return userID;
   }
   public void setUserID(Long userID) {
      this.userID = userID;
   }
   public Integer getAddrType() {
      return addrType;
   }
   public void setAddrType(Integer addrType) {
      this.addrType = addrType;
   }
   public String getStreetAddr() {
      return streetAddr;
   }
   public void setStreetAddr(String streetAddr) {
      this.streetAddr = streetAddr;
   }
   @Id @GeneratedValue
   public Long getMyId() {
      return myId;
   }
   public void setMyId(Long myId) {
      this.myId = myId;
   }
}


Top
 Profile  
 
 Post subject: Re: OneToMany bidirectional: saving new objects together
PostPosted: Mon Dec 24, 2007 1:44 pm 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
I still don't get your question. If you are trying to save by reachability then setting cascade options should do enough for you and you will have to only persist top level objects. Isn't this working for you?


Farzad-


Top
 Profile  
 
 Post subject: Re: OneToMany bidirectional: saving new objects together
PostPosted: Mon Dec 24, 2007 3:31 pm 
Newbie

Joined: Mon Dec 24, 2007 11:03 am
Posts: 3
farzad wrote:
I still don't get your question.
... you will have to only persist top level objects.
Isn't this working for you?

Exactly what I thought, but it doesn't work for me (the last save(addr) has been added as one of the many combinations). That's why I thought somebody here might shed some light on the issue.

Here's the content od the database after some tests:

Code:
mysql> select * from user; select * from address;
+----+------+----------+
| id | name | username |
+----+------+----------+
|  1 | John | blah     |
|  2 | John | blah     |
|  3 | John | blah     |
|  4 | John | blah     |
|  5 | John | blah     |
|  6 | John | blah     |
|  7 | John | blah     |
|  8 | John | blah     |
+----+------+----------+
8 rows in set (0.00 sec)

+------+----------+-----------------+--------+
| myId | addrType | streetAddr      | userID |
+------+----------+-----------------+--------+
|    1 |        0 | Prova Indirizzo |   NULL |
|    2 |        0 | Prova Indirizzo |   NULL |
|    3 |        0 | Prova Indirizzo |   NULL |
|    4 |        0 | Prova Indirizzo |   NULL |
|    5 |        0 | Prova Indirizzo |   NULL |
|    6 |        0 | Prova Indirizzo |   NULL |
|    7 |        0 | Prova Indirizzo |   NULL |
|    8 |        0 | Prova Indirizzo |   NULL |
+------+----------+-----------------+--------+
8 rows in set (0.00 sec)


Top
 Profile  
 
 Post subject: Re: OneToMany bidirectional: saving new objects together
PostPosted: Mon Dec 24, 2007 4:04 pm 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
ok. I do believe you have a problem in your mapping and that's probably why you can't do this. Here is a little test I did which shows the mapping problem:

Code:
package test.model.data.jpa;

import javax.persistence.*;
import java.util.HashMap;
import java.util.Map;

@Entity
@Table(name = "PERSON")
public class Person
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id = null;
 
    @OneToMany(mappedBy="person", cascade=CascadeType.ALL)
    @MapKey(name="addrType")
    @JoinColumn(name="PERSON_ID",referencedColumnName="ID")
    public Map<Integer,Address> address;

    public Long getId()
    {
        return id;
    }

    public void setId(Long id)
    {
        this.id = id;
    }

   public void setAddr(Address addr, int addrType) {
      if (address==null) address = new HashMap<Integer, Address>();

      addr.setAddrType(addrType);
      addr.setPerson(this);
      address.put(addrType,addr);

   }
}


JoinColumn should reference the column in address table and referencedColumnName should be primary key of person (user in your case). I do believe you can leave this to hibernate by eliminating the setting.

Address will be:

Code:
package test.model.data.jpa;

import javax.persistence.*;

@Entity
@Table(name = "ADDRESS")
public class Address
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private Long id = null;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "PERSON_ID", referencedColumnName = "ID", nullable = false, updatable = false)
    private Person person;

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

    @Column(name = "AddrType")
    private Integer addrType;

    public Long getId()
    {
        return id;
    }

    public void setId(Long id)
    {
        this.id = id;
    }

    public Person getPerson()
    {
        return person;
    }

    public void setPerson(Person person)
    {
        this.person = person;
    }

    public String getAddr1()
    {
        return addr1;
    }

    public void setAddr1(String addr1)
    {
        this.addr1 = addr1;
    }

    public Integer getAddrType()
    {
        return addrType;
    }

    public void setAddrType(Integer addrType)
    {
        this.addrType = addrType;
    }
}




My test persists everything from Person objects and there is no need to persist Address object itself. I hope this solves the problem.


Farzad-


Top
 Profile  
 
 Post subject: Re: OneToMany bidirectional: saving new objects together
PostPosted: Sat Dec 29, 2007 11:22 am 
Newbie

Joined: Mon Dec 24, 2007 11:03 am
Posts: 3
farzad wrote:
ok. I do believe you have a problem in your mapping and that's probably why you can't do this.


Yes, that's what I tnink too :-)

farzad wrote:
JoinColumn should reference the column in address table and referencedColumnName should be primary key of person (user in your case). I do believe you can leave this to hibernate by eliminating the setting.


Close inspection of your code helped me finding my mistakes:
    userID != userId (AKA hibernate is case sensitive even if mysql isn't)
    All mapped fields should be private

farzad wrote:
I hope this solves the problem.


Indeed, I thank you very much for the time you spent on my question.

Angelo.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.