Оригинальная ссылка:191119-Сохранение указанного идентификатора JPA в руководствах по серии SpringBoot
Несколько дней назад друг задал интересный вопрос: при использовании JPA для сохранения данных, даже если я указал идентификатор первичного ключа, первичным ключом вновь вставленных данных является идентификатор, который автоматически увеличивается mysql; так что же такое причина? Как это решить?
В этой статье будет рассказано, как использовать стратегию автоматического сохранения JPA для указания идентификатора первичного ключа базы данных.
I. Подготовка окружающей среды
Прежде чем приступить к работе, вам необходимо выполнить некоторые необходимые операции, такие как установка и тестирование с использованием mysql, создание проекта SpringBoot, настройка информации о конфигурации и т. д. Подробнее о сборке проекта см. в предыдущей статье.190612-Руководство по серии SpringBoot Создание базовой среды JPA
Давайте кратко рассмотрим конфигурацию, необходимую в последующем коде (мы используем базу данных mysql)
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
II. Вставить учебник
Прежде всего, давайте кратко рассмотрим: обычно мы используем метод автоинкремента базы данных по умолчанию для генерации первичных ключей, чтобы сравнить последующие стратегии генерации первичных ключей.
Для студентов, которые не очень хорошо разбираются в знаниях о вставке данных jpa, вы можете взглянуть на предыдущую запись в блоге:190614-Добавлена новая запись с использованием положения JPA в руководствах серии SpringBoot.
1. Самоувеличивающийся первичный ключ
Сначала нам нужно определить PO и привязать его к таблице в базе данных.
@Data
@DynamicUpdate
@DynamicInsert
@Entity
@Table(name = "money")
public class MoneyPO {
@Id
@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;
}
Обратите внимание, что описанная выше стратегия генерации первичного ключа используетGenerationType.IDENTITY
, с использованием mysql заключается в использовании самоинкремента базы данных для генерации идентификатора первичного ключа.
/**
* 新增数据
* Created by @author yihui in 11:00 19/6/12.
*/
public interface MoneyCreateRepositoryV2 extends JpaRepository<MoneyPO, Integer> {
}
Далее, сохранить данные легко
private void addWithId() {
MoneyPO po1 = new MoneyPO();
po1.setId(20);
po1.setName("jpa 一灰灰 1x");
po1.setMoney(2200L + ((long) (Math.random() * 100)));
po1.setIsDeleted((byte) 0x00);
MoneyPO r1 = moneyCreateRepositoryV2.save(po1);
System.out.println("after insert res: " + r1);
}
Настоятельно рекомендуется проверить выполнение приведенного выше кода.
Первое выполнение гарантирует, что в базе данных нет записи с идентификатором 20. Хотя в нашем объекте PO указан идентификатор 20, после завершения выполнения идентификатор вновь добавленных данных не равен 20.
Hibernate: select moneypo0_.id as id1_0_0_, moneypo0_.create_at as create_a2_0_0_, moneypo0_.is_deleted as is_delet3_0_0_, moneypo0_.money as money4_0_0_, moneypo0_.name as name5_0_0_, moneypo0_.update_at as update_a6_0_0_ from money moneypo0_ where moneypo0_.id=?
Hibernate: insert into money (is_deleted, money, name) values (?, ?, ?)
after insert res: MoneyPO(id=104, name=jpa 一灰灰 1x, money=2208, isDeleted=0, createAt=null, updateAt=null)
Выше приведен выполненный журнал sql.Обратите внимание, что вставленный sql не имеет указанного идентификатора, поэтому идентификатор вновь добавленной записи будет использовать стратегию автоматического увеличения mysql.
Когда в нашей базе данных есть запись с идентификатором 20, выполните ее снова, проверьте журнал и обнаружите, что фактическое выполнение заключается в обновлении данных.
Hibernate: select moneypo0_.id as id1_0_0_, moneypo0_.create_at as create_a2_0_0_, moneypo0_.is_deleted as is_delet3_0_0_, moneypo0_.money as money4_0_0_, moneypo0_.name as name5_0_0_, moneypo0_.update_at as update_a6_0_0_ from money moneypo0_ where moneypo0_.id=?
Hibernate: update money set create_at=?, money=?, name=?, update_at=? where id=?
after insert res: MoneyPO(id=20, name=jpa 一灰灰 1x, money=2234, isDeleted=0, createAt=null, updateAt=null)
Смело угадывайте, логика выполнения процесса сохранения следующая
- Сначала запросите соответствующие данные в базе данных в соответствии с идентификатором
- Если данных нет, добавьте их (вставьте sql без указания id)
- Если данные существуют, оценивается, есть ли изменения, чтобы определить, нужно ли их обновлять.
2. Укажите идентификатор
Затем возникает проблема, если я хочу вставить запись с указанным значением, когда в моем ПО указан идентификатор базы данных, когда в бд нет такой записи; если запись есть, обновите запись
Чтобы выполнить вышеуказанную функцию и настроить идентификатор первичного ключа, нам необходимо изменить стратегию генерации первичного ключа.Официальный предоставляет четыре
стоимость | иллюстрировать |
---|---|
GenerationType.TABLE |
Используйте определенную таблицу базы данных для хранения первичного ключа |
GenerationType.SEQUENCE |
Генерировать первичные ключи из последовательностей в базовой базе данных при условии, что база данных поддерживает последовательности. |
GenerationType.IDENTITY |
Первичный ключ автоматически генерируется базой данных (в основном тип автоматического роста) |
GenerationType.AUTO |
Первичный ключ управляется программой |
Из приведенных выше описаний четырех стратегий генерации очевидно, что мы собираемся использовать стратегию AUTO.Мы добавляем PO и указываем стратегию сохранения.
@Data
@DynamicUpdate
@DynamicInsert
@Entity
@Table(name = "money")
public class AutoMoneyPO {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "myid")
@GenericGenerator(name = "myid", strategy = "com.git.hui.boot.jpa.generator.ManulInsertGenerator")
@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;
}
При использовании индивидуальной стратегии сборки важно отметить, что@GenericGenerator(name = "myid", strategy = "com.git.hui.boot.jpa.generator.ManulInsertGenerator")
Это должно быть там, иначе выполнение вызовет исключение
Эта строка кода означает, что идентификатор первичного ключа определяетсяManulInsertGenerator
генерировать
/**
* 自定义的主键生成策略,如果填写了主键id,如果数据库中没有这条记录,则新增指定id的记录;否则更新记录
*
* 如果不填写主键id,则利用数据库本身的自增策略指定id
*
* Created by @author yihui in 20:51 19/11/13.
*/
public class ManulInsertGenerator extends IdentityGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor s, Object obj) throws HibernateException {
Serializable id = s.getEntityPersister(null, obj).getClassMetadata().getIdentifier(obj, s);
if (id != null && Integer.valueOf(id.toString()) > 0) {
return id;
} else {
return super.generate(s, obj);
}
}
}
Конкретный метод генерации первичного ключа также относительно прост.Во-первых, нужно определить, есть ли первичный ключ в ЗП, и если да, то использовать значение первичного ключа напрямую в ЗП, если нет, то использоватьIdentityGenerator
стратегия генерации первичного ключа (и эта стратегия генерации первичного ключа оказываетсяGenerationType.IDENTITY
Стратегия использования автоинкремента базы данных для генерации первичного ключа)
Далее снова тестируем вставку
// 使用自定义的主键生成策略
AutoMoneyPO moneyPO = new AutoMoneyPO();
moneyPO.setId(20);
moneyPO.setName("jpa 一灰灰 ex");
moneyPO.setMoney(2200L + ((long) (Math.random() * 100)));
moneyPO.setIsDeleted((byte) 0x00);
AutoMoneyPO res = moneyCreateRepositoryWithId.save(moneyPO);
System.out.println("after insert res: " + res);
moneyPO.setMoney(3200L + ((long) (Math.random() * 100)));
res = moneyCreateRepositoryWithId.save(moneyPO);
System.out.println("after insert res: " + res);
moneyPO = new AutoMoneyPO();
moneyPO.setName("jpa 一灰灰 2ex");
moneyPO.setMoney(2200L + ((long) (Math.random() * 100)));
moneyPO.setIsDeleted((byte) 0x00);
res = moneyCreateRepositoryWithId.save(moneyPO);
System.out.println("after insert res: " + res);
Когда приведенный выше код выполняется, убедитесь, что в базе данных нет данных с первичным ключом 20, а выходной журнал sql выглядит следующим образом
# 第一次插入
Hibernate: select automoneyp0_.id as id1_0_0_, automoneyp0_.create_at as create_a2_0_0_, automoneyp0_.is_deleted as is_delet3_0_0_, automoneyp0_.money as money4_0_0_, automoneyp0_.name as name5_0_0_, automoneyp0_.update_at as update_a6_0_0_ from money automoneyp0_ where automoneyp0_.id=?
Hibernate: insert into money (is_deleted, money, name, id) values (?, ?, ?, ?)
after insert res: AutoMoneyPO(id=20, name=jpa 一灰灰 ex, money=2238, isDeleted=0, createAt=null, updateAt=null)
# 第二次指定id插入
Hibernate: select automoneyp0_.id as id1_0_0_, automoneyp0_.create_at as create_a2_0_0_, automoneyp0_.is_deleted as is_delet3_0_0_, automoneyp0_.money as money4_0_0_, automoneyp0_.name as name5_0_0_, automoneyp0_.update_at as update_a6_0_0_ from money automoneyp0_ where automoneyp0_.id=?
Hibernate: update money set create_at=?, money=?, update_at=? where id=?
after insert res: AutoMoneyPO(id=20, name=jpa 一灰灰 ex, money=3228, isDeleted=0, createAt=null, updateAt=null)
# 第三次无id插入
Hibernate: insert into money (is_deleted, money, name) values (?, ?, ?)
after insert res: AutoMoneyPO(id=107, name=jpa 一灰灰 2ex, money=2228, isDeleted=0, createAt=null, updateAt=null)
Обратите внимание на вывод журнала выше
- Собранный sql записи при первой вставке содержит id, что также удовлетворяет требованию добавления новых данных путем указания id.
- При вставке во второй раз, поскольку запись с id=20 существует, выполняется операция обновления
- При вставке в третий раз, поскольку идентификатор отсутствует, в вставленном sql не указан идентификатор, а идентификатор первичного ключа генерируется путем самоинкремента mysql
II. Другое
0. Проекты и сообщения в блогах
-
190612-Руководство по серии SpringBoot Создание базовой среды JPA
-
190614-Добавлена новая запись с использованием положения JPA в руководствах серии SpringBoot.
-
190623 — состояние обновления JPA в серии руководств по SpringBoot.
-
190702-Подробное объяснение положения удаления JPA в серии руководств по SpringBoot.
-
190717 — основы подробного объяснения состояния запроса JPA в серии руководств по SpringBoot.
-
проект:GitHub.com/JuneB/tickets…
-
module: GitHub.com/JuneB/tickets…
1. Блог одного пепла
Это не так хорошо, как письмо.Вышеупомянутое содержание чисто из семьи.Из-за ограниченных личных способностей неизбежно есть упущения и ошибки.Если вы найдете ошибки или у вас есть лучшие предложения, вы можете критиковать и исправлять их.
Ниже представлен серый личный блог, в котором записываются все посты в блоге по учебе и работе, приглашаю всех посетить
- Блог One Ash Личный блогblog.hhui.top
- Блог One Ash - специальный весенний блогspring.hhui.top