# Cannot add or update a child row - javax.persistence.OneToOne



## bluer (8. Jul 2011)

Hi Leute ich bin hier echt am Verzweifeln!!! Ich habe folgende EntitiyKlassen Struktur:
User.java

```
import org.springframework.security.core.userdetails.UserDetails;

@Entity
public class User implements UserDetails, Serializable {
...
    @OneToOne( cascade = {CascadeType.ALL})
    @PrimaryKeyJoinColumn
    public Contact getContact() { return this.contact; }
...
}
```
Contact.java

```
@Entity
public class Contact implements Serializable {
...
    @Id
    @GeneratedValue
    public Long getId() { return this.id; }
...
}
```
Wenn ich nun einen User mit Kontaktinformationen wie folge anlege:
CustomerController.java:

```
@Controller(value="customerController")
public class CustomerController{
    ... 
    @Autowired
    private UserDao userDao;
    ...
    public void createCustomer(){
        this.user = new User();
        this.user.setContact(new Contact());
        this.exists = false;
    }

    @Transactional
    public void insertCustomer(){
        ...
            try {
                ...
                userDao.persist(user); 
                ...
            } catch (Exception e) {
                this.exists = true;
                messageController.addMessage("user_insert_exception",
                    FacesMessage.SEVERITY_ERROR);
                TransactionAspectSupport.currentTransactionStatus()
                    .setRollbackOnly();
            }
            ...
        }
    }
```
Bekomme ich folgende Fehlermeldung:

```
Cannot add or update a child row: a foreign key constraint fails (`develope`.`user`, CONSTRAINT ´FK36EBCB408F4488` FOREIGN KEY (`id`) REFERENCES `contact` (`id`))
```
Die UserDaoImpl.java sieht wie folgt aus:

```
public class UserDaoImpl implements UserDao {
	@PersistenceContext(type = PersistenceContextType.EXTENDED)
	private EntityManager em;
        
        @Override
	public void persist(User user) {
            em.persist(user);
            em.flush();
	}

}
```
Der Anwender gibt in einem JSF-Formular alle Daten ein und dies klappt auch ( durch Variablenkontrolle beim Debuggen verifiziert), aber wenn ich dann alles in die DB packen will bekomme ich die obige Fehlermeldung. Ich habe auch schon probiert die Id der Contact Tabelle selber zu setzen mit 

```
user = new User();
     contact = new Contact(user.getId());
     user.setContact(contact);
```
Jedoch hat ja der User erst eine Id, wenn der Datensatz in die Datenbank geschrieben wird. Daher gibt user.getId() bei obigen Konstruktion null zurück! Hat jmd. vllt mehr Erfahrung und hat eine Lösungsidee für mich?

Besten Dank


----------



## jwiesmann (8. Jul 2011)

Wenn du erst Contact "persistest" und dann user sollte es gehen.
Contact kann in dem Fall ja noch gar nicht existieren. Nach dem Persist hat Contact einen Datenbankeintrag und somit auch eine ID. Wenn du nun User "persistest", sollte es klappen


----------



## bluer (8. Jul 2011)

Aber ich habe doch 

```
cascade = CascadeType.All
```
gesetzt, d.h. doch, dass er automatisch, wenn er einen User einfügt auch den zugehörigen Contact einträgt. Deshalb brauch ich doch eigentlich keine ContactDao?!


----------



## jwiesmann (8. Jul 2011)

mhh .. das stimmt, hab selbst noch nie damit gearbeitet. Evtl. fehlt dir der GenerationType in der Contact Klasse.
sowas wie

```
@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long getId......
```
Ist aber das ist nur geraten, die Fehlermeldung deutet meiner Meinung nach darauf hin, dass die ID null ist.
Achso und du musst um ein Object zu persisten nicht unbedingt eine DAO dafür schreiben.

```
em.persist(ObjectXYZ)
```
funktioniert auch.


----------



## bluer (8. Jul 2011)

Der GenerationType wird automatisch mit dem Defaultwert gesetzt, das ist nicht das Problem. Incudiert CascadeType.All nicht alle Operationen, also auch em.persist?! Das ist doch der Sinn beim setzen von 
	
	
	
	





```
cascade={CascadeType.All}
```
???:L !!!


----------



## jwiesmann (8. Jul 2011)

Keine Ahnung .. probier doch mal folgendes .. änder deine UserDaoImpl zu 

```
@Override
    public void persist(Object obj) {
            em.persist(obj);
            em.flush();
    }
```
und deine insertCustomer zu 

```
@Transactional
    public void insertCustomer(){
        ...
            try {
                ...
                userDao.persist(contact);
                user.setContact(contact);
                userDao.persist(user);
```

würd mich mal interessieren obs damit klappt oder nicht. Vielleicht ist ja noch was anderes kaputt


----------



## Ebenius (8. Jul 2011)

Zeig mal den/die Contact-Konstrutor(en)!

Oder am besten Contact und User gleich vollständig.

Ebenius


----------



## bluer (8. Jul 2011)

Ich habe keinen implementiert. Es ist ja auch nicht nicht nötig, da die Id ja eine GeneratedValue Annotation hat?!


----------



## bluer (8. Jul 2011)

Wenn ich die 
	
	
	
	





```
@PrimaryKeyJoinColumn
```
 Annotion weglasse funzt es, aber das ist ja nicht das was ich will. beide tabellen sollen ja den selben primarykey haben!!!


----------



## Ebenius (8. Jul 2011)

Du hast keinen Konstruktor in Contact? Wie funktioniert dann das?
[java=2]contact = new Contact(user.getId());[/code]

Kannst Du nicht die beiden Klassen vollständig hier abwerfen?

Ebenius


----------



## Ebenius (8. Jul 2011)

Achso, das war nur ein Test, den's nicht mehr gibt.


```
@PrimaryKeyJoinColumn
```
 funktioniert doch nur bei Vererbung im 
	
	
	
	





```
JOINED
```
-Stil.

Ebenius


----------



## bluer (8. Jul 2011)

Also hier mal die vollständigen Klassen: 

```
package model.security;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.CascadeType;

import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Transient;
import model.consultant.Consultant;
import model.contact.Contact;
import model.customer.Customer;
import model.project.Project;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;

@Entity
public class User implements UserDetails, Serializable {
	private static final long serialVersionUID = -8524335377473464253L;
	private Long id;
	private String username;
	private String password;
        private Customer customer;
        private Consultant consultant;
        private Contact contact;
	private boolean enabled = true;
	private List<String> authority;
        private List<Project> projects;

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

	@Id
	@GeneratedValue
	public Long getId() {
		return id;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Override
	@Column(unique = true)
	public String getUsername() {
		return username;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String getPassword() {
		return password;
	}

        public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	@Override
	public boolean isEnabled() {
		return enabled;
	}

        public void addProject( Project project) {
            if( this.projects == null)
                this.projects = new ArrayList<Project>();
            if( !this.projects.contains(project))
                this.projects.add(project);
        }

        public void setProjects( List<Project> projects) { this.projects = projects; }

        @ManyToMany( cascade= (CascadeType.ALL))
        @JoinTable( name = "User_Project",
                    joinColumns = {@JoinColumn( name="user_id")},
                    inverseJoinColumns = {@JoinColumn (name = "project_id")})
        public List<Project> getProjects() {return this.projects; }

    @OneToOne( cascade = {CascadeType.ALL})
    //@PrimaryKeyJoinColumn
    public Contact getContact() { return this.contact; }

    public void setContact( Contact contact) { this.contact = contact;}

	/**
	 * Used for persistence.
	 * 
	 * @param authority
	 */
	public void setAuthority(List<String> authority) {
		this.authority = authority;
	}

	/**
	 * Used for persistence.
	 */
	@ElementCollection
	public List<String> getAuthority() {
		return authority;
	}

	/**
	 * Used for spring security.
	 * 
	 * @param authorities
	 */
	public void setAuthorities(Collection<GrantedAuthority> authorities) {
		if (this.authority == null)
			this.authority = new ArrayList<String>();

		this.authority.clear();
		for (GrantedAuthority grantedAuthority : authorities) {
			this.authority.add(grantedAuthority.getAuthority());
		}
	}

	/**
	 * Used for spring security.
	 */
	@Transient
	@Override
	public Collection<GrantedAuthority> getAuthorities() {
		if (authority == null)
			return new ArrayList<GrantedAuthority>();

		Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
		for (String stringAuthority : authority) {
			grantedAuthorities.add(new GrantedAuthorityImpl(stringAuthority));
		}

		return grantedAuthorities;
	}

	/**
	 * To simplify derived from {@link #enabled}.
	 */
	@Transient
	@Override
	public boolean isAccountNonExpired() {
		return enabled;
	}

	/**
	 * To simplify derived from {@link #enabled}.
	 */
	@Transient
	@Override
	public boolean isAccountNonLocked() {
		return enabled;
	}

	/**
	 * To simplify derived from {@link #enabled}.
	 */
	@Transient
	@Override
	public boolean isCredentialsNonExpired() {
		return enabled;
	}
        
        public void setCustomer ( Customer customer) { this.customer = customer; }

        @OneToOne( cascade={CascadeType.ALL})
        @JoinColumn( name="cust_id", unique=true)
        public Customer getCustomer() { return this.customer;}

        public void setConsultant ( Consultant consultant) { this.consultant = consultant; }

        @OneToOne( cascade={CascadeType.ALL})
        @JoinColumn( name="con_id", unique=true)
        public Consultant getConsultant() { return this.consultant;}

}
```


```
package model.contact;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Contact implements Serializable {

    private String telephoneNumber;
    private Long id;
    private String street;
    private Long houseNumber;
    private Long zipcode;
    private String city;

    public Contact() {}

    public Contact(Long id){
        this.id = id;
    }

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

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

    public String getTelephoneNumber() { return this.telephoneNumber; }

    public void setTelephoneNumber( String telephoneNumber) { this.telephoneNumber = telephoneNumber; }

    public String getStreet() { return this.street; }

    public void setStreet( String street) { this.street = street; }

    public String getCity() { return this.city; }

    public void setCity( String city) { this.city = city; }

    public Long getHouseNumber() { return this.houseNumber; }

    public void setHouseNumber( Long houseNumber) { this.houseNumber = houseNumber; }

    public Long getZipcode() { return this.zipcode; }

    public void setZipcode( Long zipcode) {this.zipcode = zipcode; }

}
```


----------



## Ebenius (8. Jul 2011)

Aus welchem Grund gibst Du Contact nicht eine eigene ID und machst einen Join auf eine Referenzspalte? Dann wirst Du in User.getContact() noch orphanRemoval nutzen wollen. Das wäre der übliche Weg…

Wenn Du das nicht willst, dann darfst Du in Contact die ID nicht als GeneratedValue betrachten. In User.setContact() müsstest Du außerdem, wenn User.contact nicht null ist, den alten zuerst aus dem Persistenzkontext entfernen müssen, da orphanRemoval mit joined IDs meiner Meinung nach nicht funktionieren kann. Außerdem musst Du dann beim PostPersist-Event die id im Contact setzen. Und der CascadeType ALL funktioniert dann auch nicht (denke ich); Du kannst wahrscheinlich nur REFRESH, REMOVE, MERGE und DETACH nehmen.

PrimaryKeyJoinColumn ist in jedem Fall falsch.

Ebenius


----------



## bluer (8. Jul 2011)

Habe das jetzt  über die Referenzspalte gelöst und läuft wunderbar! Danke an alle, die sich an der Debatte zur Problemlösung beteiligt haben!!!


----------

