봄 부팅
2.4.0
, DB는 MySql입니다.
8
.
REST를 사용하여 원격에서 15 초마다 데이터를 가져 와서 MySql DB에 저장합니다.
saveAll()
.
주어진 모든 엔티티에 대해 save () 메소드를 호출합니다..
모든 데이터에는 세트 ID가 있습니다.
그리고 DB에 그러한 ID가 없으면삽입.
이러한 ID가 이미 DB에 제시되어 있다면업데이트 됨.
다음은 콘솔에서 가져온 것입니다.
Hibernate:
insert
into
iot_entity
(controller_ref, description, device_id, device_ref, entity_type_ref, hw_address, hw_serial, image_ref, inventory_nr, ip6address1, ip6address2, ip_address1, ip_address2, latlng, location, mac_address, name, params, status, tenant, type, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
...
2020-12-05 23:18:28.269 ERROR 15752 --- [ restartedMain] o.h.e.jdbc.batch.internal.BatchingBatch : HHH000315: Exception executing batch [java.sql.BatchUpdateException: Duplicate entry '1' for key 'iot_entity.PRIMARY'], SQL: insert into iot_entity (controller_ref, description, device_id, device_ref, entity_type_ref, hw_address, hw_serial, image_ref, inventory_nr, ip6address1, ip6address2, ip_address1, ip_address2, latlng, location, mac_address, name, params, status, tenant, type, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-12-05 23:18:28.269 WARN 15752 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1062, SQLState: 23000
2020-12-05 23:18:28.269 ERROR 15752 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : Duplicate entry '1' for key 'iot_entity.PRIMARY'
2020-12-05 23:18:28.269 DEBUG 15752 --- [ restartedMain] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback after commit exception
org.springframework.dao.DataIntegrityViolationException: could not execute batch; SQL [insert into iot_entity (controller_ref, description, device_id, device_ref, entity_type_ref, hw_address, hw_serial, image_ref, inventory_nr, ip6address1, ip6address2, ip_address1, ip_address2, latlng, location, mac_address, name, params, status, tenant, type, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]; constraint [iot_entity.PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute batch
가져오고 저장하는 방법은 다음과 같습니다.
@Override
@SneakyThrows
@Scheduled(fixedDelay = 15_000)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void fetchAndStoreData() {
IotEntity[] entities = restTemplate.getForObject(properties.getIotEntitiesUrl(), IotEntity[].class);
log.debug("ENTITIES:\n{}", mapper.writerWithDefaultPrettyPrinter().writeValueAsString(entities));
if (entities != null && entities.length > 0) {
entityRepository.saveAll(List.of(entities));
} else {
log.warn("NO entities data FETCHED !!!");
}
}
이 방법은15 초마다.
실재:
@Data
@Entity
@NoArgsConstructor
@EqualsAndHashCode(of = {"id"})
@ToString(of = {"id", "deviceId", "entityTypeRef", "ipAddress1"})
public class IotEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Integer id;
// other fields
및 저장소 :
public interface EntityRepository extends JpaRepository<IotEntity, Integer> {
}
다음은 JSON 형식의 iot 엔티티에 대한 내용입니다.
2020-12-05 23:18:44.261 DEBUG 15752 --- [pool-3-thread-1] EntityService : ENTITIES:
[ {
"id" : 1,
"controllerRef" : null,
"name" : "Local Controller Unterföhring",
"description" : "",
"deviceId" : "",
...
그래서 ID는 확실히 설정되었습니다.
또한 프로젝트에 대해 일괄 처리가 활성화됩니다. 저축에 영향을 미치지 않아야합니다.
기존 항목을 업데이트하는 대신 새 항목을 삽입하려는 이유를 이해할 수 없습니까?
왜 이전 엔티티와 새 엔티티의 차이를 구별 할 수 없습니까?
최신 정보:
엔티티에 대한 지속성 구현 :
@Data
@Entity
@NoArgsConstructor
@EqualsAndHashCode(of = {"id"})
@ToString(of = {"id", "deviceId", "entityTypeRef", "ipAddress1"})
public class IotEntity implements Serializable, Persistable<Integer> {
private static final long serialVersionUID = 1L;
@Id
private Integer id;
@Override
public boolean isNew() {
return false;
}
@Override
public Integer getId() {
return this.id;
}
그러나 동일한 예외로 실패합니다.
Duplicate entry '1' for key 'iot_entity.PRIMARY'
내가 추가한다면
@GeneratedValue
다음과 같이 :
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
실패하지 않을 것입니다. 그러나 자체적으로 ID 값을 업데이트합니다.
예를 들어,
id = 15
:
[ {
"id" : 15,
"carParkRef" : 15,
"name" : "UF Haus 1/2",
다음과 같이 저장해야합니다.
사실 그것은
id = 2
대신 :
그리고 그것은 올바르지 않습니다.
<시간 />저장 서비스에 추가하려고했습니다.
private final EntityManager entityManager;
...
List.of(carParks).forEach(entityManager::merge);
동일한 예외 (Persistable 구현 여부에 관계없이)와 함께 실패합니다. 값을 삽입하려고합니다.
insert into ... Duplicate entry '15' for key '... .PRIMARY'
스 니펫
application.yml
:
spring:
# ===============================
# = DATA SOURCE
# ===============================
datasource:
url: jdbc:mysql://localhost:3306/demo_db
username: root
password: root
initialization-mode: always
# ===============================
# = JPA / HIBERNATE
# ===============================
jpa:
show-sql: true
generate-ddl: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
generate_statistics: true
여기에서 볼 수 있습니다pom 파일 내용.
이 문제를 해결하는 방법은 무엇입니까?
- 답변 # 1
- 답변 # 2
이 행동의 근원을 찾은 것 같습니다.
기본 앱 런처는 다음과 같습니다.
@AllArgsConstructor @SpringBootApplication public class Application implements CommandLineRunner { private final DataService dataService; private final QrReaderServer qrReaderServer; private final MonitoringService monitoringService; @Override public void run(String... args) { dataService.fetchAndStoreData(); monitoringService.launchMonitoring(); qrReaderServer.launchServer(); }
세 단계 모두 엄격한 실행 순서가 있습니다. 그리고 첫 번째는 필요한 경우 데이터를 로컬로 업데이트하기 위해 반복해야합니다. 저장된 데이터로만 작동하는 다른 두 대의 서버.
첫 번째 방법은 다음과 같습니다.
@Scheduled(fixedDelay = 15_000) public void fetchAndStoreData() { log.debug("START_DATA_FETCH"); carParkService.fetchAndStoreData(); entityService.fetchAndStoreData(); assignmentService.fetchAndStoreData(); permissionService.fetchAndStoreData(); capacityService.fetchAndStoreData(); log.debug("END_DATA_FETCH"); }
또한이 실행도 예정되어 있습니다.
앱이 시작될 때이 가져 오기를 두 번 실행하려고했습니다.
2020-12-14 14:00:46.208 DEBUG 16656 --- [pool-3-thread-1] c.s.s.s.data.impl.DataServiceImpl : START_DATA_FETCH 2020-12-14 14:00:46.208 DEBUG 16656 --- [ restartedMain] c.s.s.s.data.impl.DataServiceImpl : START_DATA_FETCH
2 개의 스레드가 동일한 캐치에서 실행되고 병렬로 저장됩니다.
insert
데이터. (테이블은 시작할 때마다 다시 생성됩니다).이후의 모든 가져 오기는 괜찮습니다.
@Sceduled
실.만약 코멘트
<시간 />@Sceduled
-예외없이 잘 작동합니다.해결책:
서비스 클래스에 추가 부울 속성을 추가했습니다.
@Getter private static final AtomicBoolean ifDataFetched = new AtomicBoolean(false); @Override @Scheduled(fixedDelay = 15_000) @Order(value = Ordered.HIGHEST_PRECEDENCE) public void fetchAndStoreData() { log.debug("START_DATA_FETCH"); ... log.debug("END_DATA_FETCH"); ifDataFetched.compareAndExchange(false, true); }
그리고 응용 프로그램이 시작된 후 값을 제어하십시오.
@Override public void run(String... args) { waitRemoteDataStoring(); monitoringService.launchMonitoring(); qrReaderServer.launchServer(); } private void waitRemoteDataStoring() { do { try { Thread.sleep(1_000); counter += 1; log.debug("wait for data fetch one more second ..."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } while (!DataServiceImpl.getIfDataFetched().get() || counter > DATA_FETCH_LIMIT); log.debug("Data is fetched and stored, launch the services now"); }
문제는 아마도
@Id
로 표시되지 않음@GeneratedValue
, SpringData는 모든 분리 된 (일시적인) 엔티티가save()/saveAll()
있어야한다EntityManager.persist()
그들에게 호출되었습니다.만들어보십시오
IotEntity
도구Persistable
및 반환false
...에서isNew()
. 이것은 SpringData에게 항상EntityManager.merge()
대신 원하는 효과를 가져야합니다 (예 : 존재하지 않는 엔티티 삽입 및 기존 엔티티 업데이트).