Bonjour à tous,

je désire gérer une relation Père-Fils, en enlevant un des fils.
Quelque chose comme ça :
monPere.getFils(); // 2 fils :
// hypothese : monFils1 est evidemment un des 2 fils de monPere
monPere.getFils().remove(monFils1);
monPereDAO.save(monPere);
// ici, en base, il n'y a plus que le Pere et un seul de ces fils

Voici ce que j'ai base :
T_PERE
ID : INTEGER /**cle primaire, non null*/
NAME : VARCHAR

T_FILS
ID : INTEGER /**cle primaire, non null*/
PERE_ID : INTEGER /**Cle etrangere vers T_PERE, non null */
NAME : VARCHAR

et en code Java :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
@Entity
@Table(name = "T_PERE")
public class Pere implements Serializable {
	private static final long serialVersionUID = 1L;
 
	/** The id. */
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pere_seq")
	@SequenceGenerator(name = "pere_seq", allocationSize = 10, sequenceName = "SEQ_PERE_ID")
	private Long id;
 
	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinColumn(name = "PERE_ID")
	private Set<Fils> fils = new HashSet<Fils>();
 
	public Long getId() {
		return id;
	}
 
	public void setId(Long id) {
		this.id = id;
	}
 
	public void setFils(Set<Fils> fils) {
		this.fils = fils;
 
		for (Fils aFils : this.filss) {
			aFils .setPere(this);
		}
	}
 
	public Set<Fils> getFils() {
		return fils;
	}
 
 
	// ---------------------------------------------------------
 
	public void addFils(Fils aFils) {
		if (fils == null) {
			fils = new HashSet<Fils>();
		}
		fils.add(aFils);
		aFils.setPere(this);
	}
 
 
	public boolean remove(Fils aFils) {
		// aFils.setPere(null);
		return fils.remove(aFils);
	}
}
 
@Entity
@Table(name = "T_FILS")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Fils implements Serializable {
	private static final long serialVersionUID = 1L;
 
	/** The id. */
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "fils_seq")
	@SequenceGenerator(name = "fils_seq", allocationSize = 10, sequenceName = "SEQ_FILS_ID")
	private Long id;
 
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "PERE_ID", nullable = false)
	private Pere pere;
 
	public Long getId() {
		return id;
	}
 
	public void setId(Long id) {
		this.id = id;
	}
 
	public Pere getPere() {
		if (pere instanceof HibernateProxy) {
			Object r = ((HibernateProxy) pere).getHibernateLazyInitializer().getImplementation();
			return (Pere) r;
		} else {
			return pere;
		}
	}
 
	public void setPere(Pere pere) {
		this.pere = pere;
	}
 
 
	// -----------------------------------------------------------
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
 
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Fils other = (Fils) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id)) {
			return false;
		}
		return true;
	}
}
Le code "DAO" est du type suivant :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@Repository
public class PereDAO {
	public static Logger LOGGER = Logger.getLogger(PereDAO.class);
 
	@PersistenceContext
	protected EntityManager em;
 
	/**
         * Private constructor used to prevent from instantiation.
         * 
         * Use DAOFactory.
         */
	private PereDAO() {
		super();
	}
 
	public Class<Pere> getModelClass() {
		return Pere.class;
	}
 
	public Pere getById(Long id) throws DAOException {
		try {
			return em.find(getModelClass(), id);
		} catch (Exception e) {
			logger.error("Error while getting by id: " + id + " on " + getModelClass(), e);
			throw new DAOException(e);
		}
	}
 
	@SuppressWarnings("unchecked")
	public List<Pere> findAll() throws DAOException {
		try {
			String q = "SELECT e FROM " + getModelClass().getSimpleName() + " e";
			Query query = em.createQuery(q);
			return (List<Pere>) query.getResultList();
		} catch (Exception e) {
			logger.error("Error while fetching all on " + getModelClass(), e);
			throw new DAOException(e);
		}
	}
 
	public void save(Pere object) throws DAOException {
		try {
			if (em.contains(object)) {
				em.merge(object);
			} else {
				em.persist(object);
			}
 
			em.flush();
		} catch (Exception e) {
			logger.error("Error while saving: " + object + " on " + getModelClass(), e);
			throw new DAOException(e);
		}
	}
 
	public void merge(Pere object) throws DAOException {
		try {
			em.merge(object);
		} catch (Exception e) {
			logger.error("Error while saving: " + object + " on " + getModelClass(), e);
			throw new DAOException(e);
		}
	}
 
	public void flush() throws DAOException {
		try {
			em.flush();
		} catch (Exception e) {
			logger.error("Error while flushing on: " + getModelClass(), e);
			throw new DAOException(e);
		}
	}
 
	public void delete(E object) throws DAOException {
		try {
			object = em.merge(object);
			em.remove(object);
		} catch (Exception e) {
			logger.error("Error while deleting: " + object + " on " + getModelClass(), e);
			throw new DAOException(e);
		}
	}
}
Dans ma classe de test, j'essaye de recupérer le père, puis d'enlever ses 2 fils, puis d'effacer le pere :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
			Assert.assertEquals(2, monPere.getFils().size()); // ça passe
			Set<Fils> allFils = new HashSet<Fils>(
					monPere.getFils());
			for (Fils monFils : allFils) {
				monPere.remove(monFils);
				// filsDAO.delete(monFils);
			}
			pereDao.delete(pere);
Toutefois, à l'exécution, j'ai quand même l'erreur suivante :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
// ... 
Caused by: java.sql.BatchUpdateException: ORA-01407: impossible de mettre à jour ("BASE_OWNER"."T_FILS"."PERE_ID") avec NULL
J'ai la même erreur, y compris quand j'essaye d'effacer le fils directement avant d'effacer le père (cf code de test, en dé-commentant le code "filsDAO.delete(monFils)".

Question : Que faut il modifier pour que je puisse effacer un ou tous les fils d'un père ? Idem si je veux effacer directement toute l'arborescence (pere + fils) ?

Merci d'avance pour vos éclaircissements et vos idées de débugage.

Cordialement,