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

bluer

Aktives Mitglied
Hi Leute ich bin hier echt am Verzweifeln!!! Ich habe folgende EntitiyKlassen Struktur:
User.java
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
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:
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:
SQL:
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:
Java:
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
Java:
     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

Bekanntes Mitglied
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

Aktives Mitglied
Aber ich habe doch
Java:
 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

Bekanntes Mitglied
mhh .. das stimmt, hab selbst noch nie damit gearbeitet. Evtl. fehlt dir der GenerationType in der Contact Klasse.
sowas wie
Java:
    @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.
Java:
em.persist(ObjectXYZ)
funktioniert auch.
 

bluer

Aktives Mitglied
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
Java:
cascade={CascadeType.All}
???:L !!!
 

jwiesmann

Bekanntes Mitglied
Keine Ahnung .. probier doch mal folgendes .. änder deine UserDaoImpl zu
Java:
     @Override
    public void persist(Object obj) {
            em.persist(obj);
            em.flush();
    }
und deine insertCustomer zu
Java:
    @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 :)
 

bluer

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

bluer

Aktives Mitglied
Wenn ich die
Java:
@PrimaryKeyJoinColumn
Annotion weglasse funzt es, aber das ist ja nicht das was ich will. beide tabellen sollen ja den selben primarykey haben!!!
 

Ebenius

Top Contributor
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
 
Zuletzt bearbeitet:

Ebenius

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

Code:
@PrimaryKeyJoinColumn
funktioniert doch nur bei Vererbung im
Code:
JOINED
-Stil.

Ebenius
 

bluer

Aktives Mitglied
Also hier mal die vollständigen Klassen:
Java:
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;}

}

Java:
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

Top Contributor
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

Aktives Mitglied
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!!!
 

Ähnliche Java Themen

Neue Themen


Oben