skip to Main Content

There are two tables:

        User table `user`
        +----+----------------------------+-------------+
        | id | date_created_user          | email       |
        +----+----------------------------+-------------+
        |  7 | 2023-02-23 13:23:09.085897 | [email protected] |
        | 16 | 2023-02-25 14:23:31.691560 | [email protected] |
        | 17 | 2023-02-25 14:24:02.089010 | [email protected] |
        | 18 | 2023-02-25 14:24:24.708500 | [email protected] |
        | 19 | 2023-02-25 14:25:19.253770 | [email protected] |
        +----+----------------------------+-------------+


        Deletion table `deletion`
        +----+----------------+----------------------------+---------+
        | id | active         | date                       | user_id |
        +----+----------------+----------------------------+---------+
        | 10 | false          | 2023-02-25 14:23:31.691560 |      16 |
        | 11 | false          | 2023-02-25 14:24:02.089010 |      17 |
        | 12 | true           | 2023-02-25 14:24:24.708500 |      18 |
        | 13 | true           | 2023-02-25 14:25:19.253770 |      19 |
        +----+----------------+----------------------------+---------+




        Relationship table `user_role`
        +---------+---------------+
        | user_id | role_id       |
        +---------+---------------+
        |       7 |            1  |
        |      16 |            2  |
        |      17 |            2  |
        |      18 |            2  |
        |      19 |            2  |
        +---------+---------------+



    DESCRIBE User `user`
    +-------------------------+-------------+------+-----+---------+----------------+
    | Field                   | Type        | Null | Key | Default | Extra          |
    +-------------------------+-------------+------+-----+---------+----------------+
    | id                      | bigint      | NO   | PRI | NULL    | auto_increment |
    | email                   | varchar(58) | NO   | UNI | NULL    |                |
    | enabled                 | bit(1)      | NO   |     | NULL    |                |
    | password                | varchar(65) | NO   |     | NULL    |                |
    | token                   | varchar(45) | YES  | UNI | NULL    |                |
    +-------------------------+-------------+------+-----+---------+----------------+



    DESCRIBE Deletion `deletion`
    +--------------------+-------------+------+-----+---------+----------------+
    | Field              | Type        | Null | Key | Default | Extra          |
    +--------------------+-------------+------+-----+---------+----------------+
    | id                 | bigint      | NO   | PRI | NULL    | auto_increment |
    | active             | bit(1)      | NO   |     | NULL    |                |
    | date               | datetime(6) | NO   |     | NULL    |                |
    | user_id            | bigint      | YES  | MUL | NULL    |                |
    +--------------------+-------------+------+-----+---------+----------------+


    DESCRIBE  `user_role`
    +---------------+--------+------+-----+---------+-------+
    | Field         | Type   | Null | Key | Default | Extra |
    +---------------+--------+------+-----+---------+-------+
    | user_id       | bigint | NO   | PRI | NULL    |       |
    | role_id       | bigint | NO   | PRI | NULL    |       |
    +---------------+--------+------+-----+---------+-------+

It is necessary to delete certain records from these tables.

The condition is the following:

If in the Deletion table, the active field contains the value false and more than 24 hours have passed in the date field, then you need to delete this record and delete the record from the User table.

The user_id key in the Deletion table.

And also, there is a table of relations user_role. This is a table of the user and his role. It will not be possible to delete a user if there is a relationship. Therefore, it is also necessary to remove this relation.

My repository

    @Transactional(readOnly = true)
    @Repository
    public interface DeletionRepository extends JpaRepository<Deletion, Long> {



        @Transactional
        @Modifying
        @Query("DELETE FROM Deletion as a WHERE a.active = false AND a.date <= :date")
        void deleteDeletionByActiveAndDate(@Param("date") String date);


    }

2

Answers


  1. Given that you need to do some datetime manipulations in your delete query, which tend to be highly database specific, I suggest using the following native MySQL query:

    DELETE
    FROM User u
    WHERE EXISTS (
        SELECT 1
        FROM Deletion d
        WHERE d.user_id = u.id AND
              d.active = false AND
              d.date < NOW() - INTERVAL 1 DAY
    );
    

    If you also want to delete the record from the Deleted table, then setup a cascading foreign key on user_id, such that deleting the parent record from the User table will also delete the dependent record.

    If you can’t setup cascading deletion, on MySQL you could also use the following delete join:

    DELETE u, d
    FROM User u
    INNER JOIN Deleted d
        ON d.user_id = u.id
    WHERE d.active = false AND
          d.date < NOW() - INTERVAL 1 DAY;
    
    Login or Signup to reply.
  2. NOTE: I wrote this answer partially with the assistance of GitHub Copilot.


    TL;DR: Use an appropriate CascadeType such as REMOVE or ALL on your Deletion entity.


    If you define the User and Deletion entities as follows:

    package org.behrang.stackoverflow.q75563217.entity;
    
    import jakarta.persistence.*;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    @Entity
    @Table(name = "Users")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private int id;
    
        private Date createdAt;
    
        private String email;
    
        @OneToMany(cascade = CascadeType.PERSIST)
        private List<Deletion> deletions = new ArrayList<>();
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public Date getCreatedAt() {
            return createdAt;
        }
    
        public void setCreatedAt(Date createdAt) {
            this.createdAt = createdAt;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    
        public List<Deletion> getDeletions() {
            return deletions;
        }
    
        public void setDeletions(List<Deletion> deletions) {
            this.deletions = deletions;
        }
    
        public void addDeletion(Deletion deletion) {
            this.deletions.add(deletion);
        }
    }
    
    package org.behrang.stackoverflow.q75563217.entity;
    
    import jakarta.persistence.*;
    
    import java.util.Date;
    
    @Entity
    @Table(name = "Deletions")
    public class Deletion {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        private boolean active;
    
        private Date date;
    
        @ManyToOne(cascade = CascadeType.REMOVE)
        @JoinColumn(name = "user_id")
        private User user;
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public Long getId() {
            return id;
        }
    
        public boolean isActive() {
            return active;
        }
    
        public void setActive(boolean active) {
            this.active = active;
        }
    
        public Date getDate() {
            return date;
        }
    
        public void setDate(Date date) {
            this.date = date;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    }
    

    And use the following persistence.xml file:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <persistence xmlns="https://jakarta.ee/xml/ns/persistence"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
                 version="3.0">
        <persistence-unit name="default">
            <class>org.behrang.stackoverflow.q75563217.entity.User</class>
            <class>org.behrang.stackoverflow.q75563217.entity.Deletion</class>
            <properties>
                <property name="jakarta.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
                <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:32773/example"/>
                <property name="jakarta.persistence.jdbc.user" value="root"/>
                <property name="jakarta.persistence.jdbc.password" value="password"/>
                <property name="jakarta.persistence.schema-generation.database.action" value="create"/>
            </properties>
        </persistence-unit>
    </persistence>
    

    And use this schema (generated by Hibernate automatically):

    create table if not exists Deletions_SEQ
    (
        next_val bigint null
    );
    
    create table if not exists Users
    (
        id        int          not null primary key,
        createdAt datetime(6)  null,
        email     varchar(255) null
    );
    
    create table if not exists Deletions
    (
        id      bigint      not null primary key,
        active  bit         not null,
        date    datetime(6) null,
        user_id int         null,
        constraint FK2bq97txm7h2eirrkbv84xpv2y foreign key (user_id) references Users (id)
    );
    
    create table if not exists Users_Deletions
    (
        User_id      int    not null,
        deletions_id bigint not null,
        constraint UK_jhg9je4j0jwu07wpjw2yvm9ve unique (deletions_id),
        constraint FK98pxufvij990xbgyihq7lxn2a foreign key (User_id) references Users (id),
        constraint FKg1q9bnqdsn40gecsagv32b16q foreign key (deletions_id) references Deletions (id)
    );
    
    create table if not exists Users_SEQ
    (
        next_val bigint null
    );
    

    Then this demo code shows how the cascading effect works:

    package org.behrang.stackoverflow.q75563217;
    
    import jakarta.persistence.EntityManager;
    import jakarta.persistence.EntityManagerFactory;
    import jakarta.persistence.EntityTransaction;
    import jakarta.persistence.Persistence;
    import org.behrang.stackoverflow.q75563217.entity.Deletion;
    import org.behrang.stackoverflow.q75563217.entity.User;
    
    import java.sql.Date;
    import java.time.Duration;
    import java.time.Instant;
    
    public class Demo {
        public static void main(String[] args) {
            EntityManagerFactory factory = Persistence.createEntityManagerFactory("default");
            EntityManager entityManager = factory.createEntityManager();
            EntityTransaction tx = entityManager.getTransaction();
            tx.begin();
    
            Instant i1 = Instant.now().minus(Duration.ofHours(25));
            User u1 = new User();
            u1.setEmail("[email protected]");
            u1.setCreatedAt(Date.from(i1));
    
            Deletion d01 = new Deletion();
            d01.setDate(Date.from(Instant.now()));
            d01.setUser(u1);
            u1.addDeletion(d01);
    
            entityManager.persist(u1);
    
            tx.commit();
            entityManager.close();
    
            entityManager = factory.createEntityManager();
            tx = entityManager.getTransaction();
            tx.begin();
    
            Deletion d11 = entityManager.find(Deletion.class, 1L);
            entityManager.remove(d11);
    
            tx.commit();
            entityManager.close();
    
            factory.close();
        }
    }
    

    Demo video: https://www.youtube.com/watch?v=PCZcJdi9wf8.

    Sample code: https://github.com/behrangsa/so-75563217

    For the age comparison, you can add the date/time logic to your JPQL query.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search