Reputation: 8796
I use JPA with Hibernate.
I have a User class and a Flag class :
class User
{
// ...
}
class Flag
{
@OneToOne(optional=false)
private User user;
}
I dont want/need a reference to the Flag object in the User class (and that's the main point). But when I delete a User, I'd like the associated Flag (if there's one) to be deleted too, without any foreign key constraint fails exception!
I know I could add a reference to the Flag inside the User, like :
class User
{
@OneToOne(optional=true, cascade=CascadeType.ALL, mappedBy="user")
@org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Flag flag;
}
And then the Flag object would be deleted when I delete the user.
But is it possible to cascade delete the Flag without having a reference to it in the User?
If not, what if the correct way to delete such an object?
Upvotes: 0
Views: 3020
Reputation: 10119
You didn't say which database you're using, so I'll assume it to be MySQL.
These are our tables. Note the one-to-one mapping between a user_id and a user_flag_id and the on delete cascade on update cascade clause.
CREATE TABLE `user` (
`user_id` int(11) NOT NULL DEFAULT '0',
`user_name` varchar(32) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB;
CREATE TABLE `user_flag` (
`user_flag_id` int(11) NOT NULL DEFAULT '0',
`user_flag_name` varchar(32) DEFAULT NULL,
PRIMARY KEY (`user_flag_id`),
CONSTRAINT `fk_user_id` FOREIGN KEY (`user_flag_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
Given these, you can create mappings like the followings.
@Entity(name = "user")
@Table(schema = "test", name = "user")
public final class User {
@Id
@Column(name = "user_id")
public int id;
@Basic
@Column(name = "user_name")
public String name;
}
@Entity(name = "user_flag")
@Table(schema = "test", name = "user_flag")
public final class UserFlag {
@Id
@Column(name = "user_flag_id")
public int id;
@Basic
@Column(name = "user_flag_name")
public String name;
@OneToOne
@JoinColumn(name = "user_flag_id", referencedColumnName = "user_id")
public User user;
}
Let's assume we have the following data.
+---------+-----------+
| user_id | user_name |
+---------+-----------+
| 1 | Joe |
| 2 | Jane |
| 3 | Jacob |
+---------+-----------+
+--------------+----------------+
| user_flag_id | user_flag_name |
+--------------+----------------+
| 1 | Joe's flag |
| 3 | Jacob's flag |
+--------------+----------------+
The following code will demonstrate that without any hassle, if you delete a user its associated flag will be deleted (but not the other way around).
@Test
public void test() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test");
EntityManager em = emf.createEntityManager();
EntityTransaction et = em.getTransaction();
// prints out our three users, Joe, Jane and Jacob
for (User u : em.createQuery(String.format("select u from %s u", User.class.getName()), User.class)
.getResultList())
System.out.format("User{id=%s, name=%s}\n", u.id, u.name);
// prints out Joe's and Jacob's flags
for (UserFlag uf : em.createQuery(String.format("select uf from %s uf", UserFlag.class.getName()), UserFlag.class)
.getResultList())
System.out.format("UserFlag{id=%s, name=%s}\n", uf.id, uf.name);
et.begin();
User jacob = em.find(User.class, 3);
em.remove(jacob);
et.commit();
// prints out our remaining users, Joe and Jane
for (User u : em.createQuery(String.format("select u from %s u", User.class.getName()), User.class)
.getResultList())
System.out.format("User{id=%s, name=%s}\n", u.id, u.name);
// prints out Joe's flag
for (UserFlag uf : em.createQuery(String.format("select uf from %s uf", UserFlag.class.getName()), UserFlag.class)
.getResultList())
System.out.format("UserFlag{id=%s, name=%s}\n", uf.id, uf.name);
em.close();
emf.close();
}
This example assumes that your engine of choice is InnoDB. If however you've wanted to use MyISAM you can simulate the on update cascade on delete cascade clause using triggers.
Upvotes: 0
Reputation: 7048
You can't cascade the delete automatically in this case, you'll need to implement your own delete.
When you delete a user, first run a query like:
delete from Flag F where F.userId = :userId
Upvotes: 1