Reputation: 69
In a Spring Boot application I tried directly inserting in SQL Server.
CREATE TABLE T_FLATTEN_PROCESSING_INFO(
[HOST_ID] [varchar](150),
[BEGIN_VERSION] [bigint],
[END_VERSION] [bigint],
PROCESSING_DT_TM DATETIME2,
PRIMARY KEY (BEGIN_VERSION, END_VERSION)
)
Repository
@Repository
public interface FlattenProcessingInfoRepo extends JpaRepository<TableFlattenProcessingInfo, TableFlattenProcessingInfoCPK> {
}
Entity Object
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "T_FLATTEN_PROCESSING_INFO")
public class TableFlattenProcessingInfo {
@EmbeddedId
private TableFlattenProcessingInfoCPK tableFlattenProcessingInfoCPK;
@Column(name = "HOST_ID")
private String hostId;
@Column(name = "PROCESSING_DT_TM")
@DateTimeFormat(pattern = "MM/DD/YYYY HH:mm:ss")
private LocalDateTime processingDateTime;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Embeddable
public class TableFlattenProcessingInfoCPK {
@Column(name = "BEGIN_VERSION")
private long beginVersion;
@Column(name = "END_VERSION")
private long endVersion;
}
Service
@Transactional(transactionManager = "standardizedDataSourceTransactionManager", propagation = Propagation.REQUIRES_NEW)
public boolean processSave(long beginVersion, long endVersion, String hostId) {
boolean success = false;
try {
TableFlattenProcessingInfo tableFlattenProcessingInfo = new TableFlattenProcessingInfo();
TableFlattenProcessingInfoCPK tableFlattenProcessingInfoCPK = new TableFlattenProcessingInfoCPK();
tableFlattenProcessingInfoCPK.setBeginVersion(beginVersion);
tableFlattenProcessingInfoCPK.setEndVersion(endVersion);
tableFlattenProcessingInfo.setTableFlattenProcessingInfoCPK(tableFlattenProcessingInfoCPK);
tableFlattenProcessingInfo.setHostId(hostId);
tableFlattenProcessingInfo.setProcessingDateTime(DateUtil.convertUTCToEST(new Date(),
TimeZone.getTimeZone("UTC"),
TimeZone.getTimeZone("America/New_York")));
flattenProcessingInfoRepo.save(tableFlattenProcessingInfo);
success = true;
} catch (Exception e) {
log.info("Error Inserting Flatten Batch means batch already taken up for" +
" processing by another Pod -> {}", e.getMessage());
success = false;
}
return success;
}
Test
boolean retVal = flattenProcessingInfoService.processSave(1000, 2000, "localhost");
boolean retVal = flattenProcessingInfoService.processSave(1000, 2000, "localhost");
I try to insert the same record twice. It does not insert twice; I see only one record in the DB.
Why don't I get a Primary Key Violation exception the second time?
Upvotes: 0
Views: 57
Reputation: 570
You don't have any errors because method flattenProcessingInfoRepo.save(entity)
checks if an entity is new or not. If it is new, it issues an INSERT
statement; if it is old, it issues an UPDATE
statement. Take a look at SimpleJpaRepository<T, ID>.save()
:
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
Method entityInformation.isNew(entity)
is pretty simple: it checks if entity has an id
or if id
is empty. If id
is empty (null) the entity is considered to be new.
So in your case, since you manually set the id, the entity is always considered 'existing, not new' and em.merge(entity)
is called. And this method, if you dig deeper in the spring code, leads to the following events:
SELECT
query is issued to get the entity from the DBINSERT
or UPDATE
statement based on step 1: if the entity exists, it executes UPDATE
; if not, INSERT
Exception handling note. @M.Deinum in the comments is right: if an exception happens on the DB-level, you will get it only after transaction commits. And a transaction commits only after method @Transactional
finishes, i.e. after processSave(..)
returns.
So in your case it is required to handle exceptions in the service, which calls flattenProcessingInfoService.processSave(1000, 2000, "localhost");
:
try {
flattenProcessingInfoService.processSave(1000, 2000, "localhost");
} catch (Exception e) {
log.info("Error Inserting Flatten Batch means batch already taken up for processing by another Pod -> {}", e.getMessage());
}
Regarding your question in the comments, how you can check yourself if entity already exists before saving, check How to put check if record already exist - spring boot.
Upvotes: 0