Учебное пособие по серии SpringBoot JPA указало сохранение идентификатора

Spring

Оригинальная ссылка: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. Проекты и сообщения в блогах

1. Блог одного пепла

Это не так хорошо, как письмо.Вышеупомянутое содержание чисто из семьи.Из-за ограниченных личных способностей неизбежно есть упущения и ошибки.Если вы найдете ошибки или у вас есть лучшие предложения, вы можете критиковать и исправлять их.

Ниже представлен серый личный блог, в котором записываются все посты в блоге по учебе и работе, приглашаю всех посетить

一灰灰blog