оригинал:190702-Подробное объяснение положения удаления JPA в серии руководств по SpringBoot.
В общей базе данных есть четыре операции curd. В предыдущих сообщениях блога были представлены вставка и обновление соответственно. Далее давайте посмотрим на состояние удаления и на то, как удалять данные через JPA.
Вообще говоря, не рекомендуется физически удалять данные (удалять записи непосредственно из таблицы).В нынешнюю эпоху данных — это деньги, более распространено добавить поле в таблицу для представления статуса, а затем изменить это поле на представляют собой запись.Вполне ли возможно реализовать логическое удаление, причины этого следующие
- Физическое удаление, если есть проблема, это более неприятно восстановить
- Нет никакой гарантии, что код должен быть точным. Когда есть проблема, удалите неправильные данные, затем GG
- Удаление данных приведет к перестроению индекса.
- База данных InnoDB только помечает удаленные данные как удаленные и фактически не освобождает занимаемое дисковое пространство, что приводит к постоянному росту файлов базы данных InnoDB и фрагментации таблиц.
- Логическое удаление для сохранения данных для последующего извлечения или анализа данных.
I. Подготовка окружающей среды
Прежде чем начать, конечно, вы должны подготовить базовую среду, такую как использование mysql для установки и тестирования, создание проектов проектов SpringBoot, настройка информации о конфигурации и т. д. Подробную информацию о создании проектов см. в предыдущей статье.
Давайте кратко рассмотрим конфигурацию, необходимую в процессе добавления записей.
1. Подготовка стола
Следуя таблице предыдущей статьи, структура выглядит следующим образом.
CREATE TABLE `money` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
`money` int(26) NOT NULL DEFAULT '0' COMMENT '钱',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
2. Конфигурация проекта
Информация о конфигурации немного отличается от предыдущей. Мы добавили более подробную печать журнала; основная цель этой статьи — сосредоточиться на использовании состояния добавления записей. Инструкции по настройке мы объясним отдельно позже.
## DataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
## jpa相关配置
spring.jpa.database=MYSQL
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
3. Подготовка данных
Модификация данных, поэтому мы сначала вставляем две части данных в таблицу для последующих операций.
INSERT INTO `money` (`id`, `name`, `money`, `is_deleted`, `create_at`, `update_at`)
VALUES
(20, 'jpa 一灰灰5', 2323, 0, '2019-07-02 08:42:41', '2019-07-02 08:42:41'),
(21, 'jpa 一灰灰6', 2333, 0, '2019-07-02 08:42:41', '2019-07-02 08:42:41'),
(22, 'jpa 一灰灰7', 6666, 0, '2019-07-02 08:42:41', '2019-07-02 08:42:41'),
(23, 'jpa 一灰灰8', 2666, 0, '2019-07-02 08:42:41', '2019-07-02 08:42:41');
II. Удалить учебное пособие
Упомянутые ниже удаления — это все физические удаления, под которыми можно понимать прямое стирание некоторых записей из таблицы (не говоря уже о том, что после удаления нет возможности восстановиться).Для четырех операций CURD, кроме чтения, остальные три вставка, обновление и удаление добавят блокировки записи (как правило, будут задействованы блокировки строк и блокировки промежутков, как будет видно позже, эти три операции требуют отображения объявлений)
1. Ассоциация таблиц POJO
Предыдущая вставка представила пошаговый процесс создания POJO, и были определены соответствующие аннотации.Результаты размещены непосредственно ниже.
@Data
@DynamicUpdate
@DynamicInsert
@Entity
@Table(name = "money")
public class MoneyPO {
@Id
// 如果是auto,则会报异常 Table 'mysql.hibernate_sequence' doesn't exist
// @GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "money")
private Long money;
@Column(name = "is_deleted")
private Byte isDeleted;
@Column(name = "create_at")
@CreatedDate
private Timestamp createAt;
@Column(name = "update_at")
@CreatedDate
private Timestamp updateAt;
}
Несколько аннотаций в приведенном выше классе описываются следующим образом.
-
@Data
Он относится к аннотации ломбока, не имеет ничего общего с jpa и генерируется автоматически.getter/setter/equals/hashcode/tostring
и т.д. метод -
@Entity
,@Table
Аннотация jpa указывает, что этот класс связан с таблицей базы данных, а конкретное совпадение - это таблицаmoney
-
@Id
@GeneratedValue
Функция и автоинкрементный первичный ключ -
@Column
Указывает, что этот атрибут соответствует столбцу в таблице -
@CreateDate
Создание метки времени по умолчанию на основе текущего времени
2. Объявление API репозитория
Далее мы создаем новый API, который наследуется отCurdRepository
, Затем передайте этот API для работы с базой данных
public interface MoneyDeleteRepository extends CrudRepository<MoneyPO, Integer> {
/**
* 查询测试
* @param id
* @return
*/
List<MoneyPO> queryByIdGreaterThanEqual(int id);
}
3. Используйте осанку
Сначала напишите метод для запроса данных, чтобы проверить, были ли они удалены после того, как мы их удалили.
private void showLeft() {
List<MoneyPO> records = moneyDeleteRepository.queryByIdGreaterThanEqual(20);
System.out.println(records);
}
Прежде чем выполнять следующие операции, сначала вызовите вышеуказанное, и результаты вывода будут следующими:
[MoneyPO(id=20, name=jpa 一灰灰5, money=2323, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=21, name=jpa 一灰灰6, money=2333, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=22, name=jpa 一灰灰7, money=6666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=23, name=jpa 一灰灰8, money=2666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0)]
А. Удалить в соответствии с идентификатором первичного ключа
Это должен быть наиболее распространенный метод удаления. Во избежание случайного удаления рекомендуется удалять записи с помощью точного идентификатора первичного ключа.CrudRepository
Этот интерфейс уже предоставляет соответствующие методы, поэтому мы можем использовать его напрямую.
private void deleteById() {
// 直接根据id进行删除
moneyDeleteRepository.deleteById(21);
showLeft();
}
После завершения выполнения результаты вывода следующие, вы можете узнать, сравнив предыдущий выводid=21
запись пользователя была удалена
[MoneyPO(id=20, name=jpa 一灰灰5, money=2323, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=22, name=jpa 一灰灰7, money=6666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=23, name=jpa 一灰灰8, money=2666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0)]
Тогда естественно возникает вопрос, а что если записи, соответствующей этому id, не существует?
Выполните приведенный выше код еще раз и обнаружите, что возникает исключение.
Почему это так? Отлаживаемся, реализация вызова по умолчаниюSimpleJpaRepository
, его исходный код такой
// 类为: org.springframework.data.jpa.repository.support.SimpleJpaRepository
@Transactional
public void deleteById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException(
String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1)));
}
@Transactional
public void delete(T entity) {
Assert.notNull(entity, "The entity must not be null!");
em.remove(em.contains(entity) ? entity : em.merge(entity));
}
Как видно из исходного кода, это сначала опрашивается по id.Если соответствующая запись не существует, сразу выбрасывается исключение, когда оно существует, используется логика удаления;
Если мы хотим удалить несуществующие данные, не сообщайте об ошибке, что я могу сделать?
- Пользовательская реализация наследования
SimpleJpaRepository
класс, который переопределяет метод удаления
@Repository
@Transactional(readOnly = true)
public class MoneyDeleteRepositoryV2 extends SimpleJpaRepository<MoneyPO, Integer> {
@Autowired
public MoneyDeleteRepositoryV2(EntityManager em) {
this(JpaEntityInformationSupport.getEntityInformation(MoneyPO.class, em), em);
}
public MoneyDeleteRepositoryV2(JpaEntityInformation<MoneyPO, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
}
public MoneyDeleteRepositoryV2(Class<MoneyPO> domainClass, EntityManager em) {
super(domainClass, em);
}
@Override
public void deleteById(Integer id) {
Optional<MoneyPO> rec = findById(id);
rec.ifPresent(super::delete);
}
}
Затем вы можете снова вызвать вышеуказанный метод, не демонстрируя конкретный тестовый пример, исходный код можно посмотреть в проекте проекта 👉исходный код
б. Удаление условного приговора
Хотя идентификатор удалять более осмотрительно, но его нельзя избежать, в некоторых случаях необходимо удалить на основе других полей, например, мы хотим удалить имяjpa 一灰灰7
данные, то нам нужноMoneyDeleteRepository
добавить метод
/**
* 根据name进行删除
*
* @param name
*/
void deleteByName(String name);
Вот краткое упоминание правил именования этого метода, которые будут объяснены более подробно позже в запросе;
-
delete
Указывает, что выполняется операция удаления -
By
Указывает, что условия основаны на поле -
Name
Это имеет соответствие свойств в POJO
Приведенный выше способ, если переведен на SQL, эквивалентноdelete from money where name=xx
Метод вызова такой же, как и раньше, а именно:
private void deleteByName() {
moneyDeleteRepository.deleteByName("jpa 一灰灰7");
showLeft();
}
Затем мы выполнили вышеуказанный тест и обнаружили, что он не был успешным, и было сообщено об ошибке.
Изучив предыдущее обновление, я знаю, что необходимо отображать аннотацию добавления вещи, мы ее прямо здесь добавляем.Repository
середина
/**
* 根据name进行删除
*
* @param name
*/
@Transactional
void deleteByName(String name);
Затем снова выполните вывод следующим образом, здесь мы также печатаем журнал sql
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.name=?
Hibernate: delete from money where id=?
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id>=?
[MoneyPO(id=20, name=jpa 一灰灰5, money=2323, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=23, name=jpa 一灰灰8, money=2666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0)]
Из последних оставшихся записей имяjpa 一灰灰7
был удалён, а потом посмотрите на ранее удалённый sql, найдёте интересное место,deleteByName
Этот метод, переведенный в sql, становится двумя
-
select * from money where name=xxx
Первые записи запроса по имени -
delete from money where id = xxx
По идентификатору предыдущей записи запроса удалить запись
в. Сравнить удалить
Затем продемонстрируйте удаление денег в[2000,3000]
Запись интервала, то наш новый вход может быть
/**
* 根据数字比较进行删除
*
* @param low
* @param big
*/
@Transactional
void deleteByMoneyBetween(Long low, Long big);
Благодаря именованию методов вы также можете просто узнать, что приведенное выше эквивалентно sqldelete from money where money between xxx and xxx
Код теста
private void deleteByCompare() {
moneyDeleteRepository.deleteByMoneyBetween(2000L, 3000L);
showLeft();
}
выходной журнал
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.money between ? and ?
Hibernate: delete from money where id=?
Hibernate: delete from money where id=?
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id>=?
[]
Как видно из склеенного sql, приведенная выше логика эквивалентна выполнению сначала запроса, а потом удаления по одному по id....
4. Резюме
Мы удалили условия, объявив методы;
- Для удаления требуются явные операторные транзакции
@Transactional
- Удаление несуществующей записи вызовет исключение
- При объявлении метода удаления это фактически эквивалентно сначала запросу записи, а затем ее точному удалению в соответствии с идентификатором записи.
II. Другое
исходный код
- проект:GitHub.com/JuneB/tickets…
- module: GitHub.com/JuneB/tickets…
Связанные сообщения в блоге
- блокировка mysql и детали транзакции
- Использование позы весенних учебных дел
- Управление транзакциями и свойства распространения Spring Learning
- 190612-Руководство по серии SpringBoot Создание базовой среды JPA
- 190614-Добавлена новая запись с использованием положения JPA в руководствах серии SpringBoot.
- 190623 - Обновление Объявления Озера JPA в Учебниках серии Springboot
1. Серый блог
Это не так хорошо, как письмо. Вышеупомянутое содержание чисто из семьи. Из-за ограниченных личных способностей неизбежно будут упущения и ошибки. Если вы обнаружите ошибки или у вас есть лучшие предложения, вы можете критиковать и поправьте их.
Ниже представлен серый личный блог, в котором записываются все посты в блоге по учебе и работе, приглашаю всех посетить
- One Grey BlogПерсональный блогblog.hhui.top
- Серый блог - специальный весенний блогspring.hhui.top