BAEL-6581 - Skip Select Before Insert in Spring Data JPA
This commit is contained in:
parent
cc2d1bfb36
commit
6894e31e0d
|
@ -16,6 +16,11 @@
|
|||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.hypersistence</groupId>
|
||||
<artifactId>hypersistence-utils-hibernate-55</artifactId>
|
||||
<version>3.7.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.baeldung.skipselectbeforeinsert;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
import io.hypersistence.utils.spring.repository.BaseJpaRepositoryImpl;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableJpaRepositories(
|
||||
value = "com.baeldung.skipselectbeforeinsert.repository",
|
||||
repositoryBaseClass = BaseJpaRepositoryImpl.class
|
||||
)
|
||||
public class SkipSelectBeforeInsertApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SkipSelectBeforeInsertApplication.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.baeldung.skipselectbeforeinsert.model;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import org.springframework.data.domain.Persistable;
|
||||
|
||||
@Entity
|
||||
public class PersistableTask implements Persistable<Integer> {
|
||||
|
||||
@Id
|
||||
private int id;
|
||||
private String description;
|
||||
|
||||
@Transient
|
||||
private boolean isNew = true;
|
||||
|
||||
public void setNew(boolean isNew) {
|
||||
this.isNew = isNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return isNew;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.baeldung.skipselectbeforeinsert.model;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class Task {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
private String description;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.baeldung.skipselectbeforeinsert.model;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class TaskWithGeneratedId {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
private String description;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.baeldung.skipselectbeforeinsert.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.baeldung.skipselectbeforeinsert.model.PersistableTask;
|
||||
import com.baeldung.skipselectbeforeinsert.model.Task;
|
||||
|
||||
@Repository
|
||||
public interface PersistableTaskRepository extends JpaRepository<PersistableTask, Integer> {
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.baeldung.skipselectbeforeinsert.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.baeldung.skipselectbeforeinsert.model.Task;
|
||||
|
||||
import io.hypersistence.utils.spring.repository.BaseJpaRepository;
|
||||
|
||||
@Repository
|
||||
public interface TaskJpaRepository extends JpaRepository<Task, Integer>, BaseJpaRepository<Task, Integer> {
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.baeldung.skipselectbeforeinsert.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.baeldung.skipselectbeforeinsert.model.Task;
|
||||
|
||||
@Repository
|
||||
public interface TaskRepository extends JpaRepository<Task, Integer>, TaskRepositoryExtension {
|
||||
|
||||
@Modifying
|
||||
@Query(value = "insert into task(id, description) values(:#{#task.id}, :#{#task.description})", nativeQuery = true)
|
||||
void insert(@Param("task") Task task);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.baeldung.skipselectbeforeinsert.repository;
|
||||
|
||||
import com.baeldung.skipselectbeforeinsert.model.Task;
|
||||
|
||||
public interface TaskRepositoryExtension {
|
||||
Task persistAndFlush(Task task);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.baeldung.skipselectbeforeinsert.repository;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.baeldung.skipselectbeforeinsert.model.Task;
|
||||
|
||||
@Component
|
||||
public class TaskRepositoryExtensionImpl implements TaskRepositoryExtension {
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Override
|
||||
public Task persistAndFlush(Task task) {
|
||||
entityManager.persist(task);
|
||||
entityManager.flush();
|
||||
return task;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.baeldung.skipselectbeforeinsert.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.baeldung.skipselectbeforeinsert.model.Task;
|
||||
import com.baeldung.skipselectbeforeinsert.model.TaskWithGeneratedId;
|
||||
|
||||
@Repository
|
||||
public interface TaskWithGeneratedIdRepository extends JpaRepository<TaskWithGeneratedId, Integer> {
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package com.baeldung.skipselectbeforeinsert;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import com.baeldung.skipselectbeforeinsert.model.PersistableTask;
|
||||
import com.baeldung.skipselectbeforeinsert.model.Task;
|
||||
import com.baeldung.skipselectbeforeinsert.model.TaskWithGeneratedId;
|
||||
import com.baeldung.skipselectbeforeinsert.repository.PersistableTaskRepository;
|
||||
import com.baeldung.skipselectbeforeinsert.repository.TaskJpaRepository;
|
||||
import com.baeldung.skipselectbeforeinsert.repository.TaskRepository;
|
||||
import com.baeldung.skipselectbeforeinsert.repository.TaskWithGeneratedIdRepository;
|
||||
|
||||
@DataJpaTest
|
||||
public class SkipSelectBeforeInsertIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private TaskRepository taskRepository;
|
||||
@Autowired
|
||||
private TaskWithGeneratedIdRepository taskWithGeneratedIdRepository;
|
||||
@Autowired
|
||||
private PersistableTaskRepository persistableTaskRepository;
|
||||
|
||||
@Autowired
|
||||
private TaskJpaRepository taskJpaRepository;
|
||||
|
||||
@Test
|
||||
public void givenRepository_whenSaveNewTaskWithPopulatedId_thenExtraSelectIsExpected() {
|
||||
Task task = new Task();
|
||||
task.setId(1);
|
||||
taskRepository.saveAndFlush(task);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRepository_whenSaveNewTaskWithGeneratedId_thenNoExtraSelectIsExpected() {
|
||||
TaskWithGeneratedId task = new TaskWithGeneratedId();
|
||||
TaskWithGeneratedId saved = taskWithGeneratedIdRepository.saveAndFlush(task);
|
||||
assertNotNull(saved.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRepository_whenSaveNewPersistableTask_thenNoExtraSelectIsExpected() {
|
||||
PersistableTask persistableTask = new PersistableTask();
|
||||
persistableTask.setId(2);
|
||||
persistableTask.setNew(true);
|
||||
PersistableTask saved = persistableTaskRepository.saveAndFlush(persistableTask);
|
||||
assertEquals(2, saved.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRepository_whenSaveNewPersistableTasksWithSameId_thenExceptionIsExpected() {
|
||||
PersistableTask persistableTask = new PersistableTask();
|
||||
persistableTask.setId(3);
|
||||
persistableTask.setNew(true);
|
||||
persistableTaskRepository.saveAndFlush(persistableTask);
|
||||
|
||||
PersistableTask duplicateTask = new PersistableTask();
|
||||
duplicateTask.setId(3);
|
||||
duplicateTask.setNew(true);
|
||||
|
||||
assertThrows(DataIntegrityViolationException.class,
|
||||
() -> persistableTaskRepository.saveAndFlush(duplicateTask));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRepository_whenPersistNewTaskUsingCustomPersistMethod_thenNoExtraSelectIsExpected() {
|
||||
Task task = new Task();
|
||||
task.setId(4);
|
||||
Task saved = taskRepository.persistAndFlush(task);
|
||||
|
||||
assertEquals(4, saved.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRepository_whenPersistNewTaskUsingPersist_thenNoExtraSelectIsExpected() {
|
||||
Task task = new Task();
|
||||
task.setId(5);
|
||||
Task saved = taskJpaRepository.persistAndFlush(task);
|
||||
|
||||
assertEquals(5, saved.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRepository_whenPersistTaskWithTheSameId_thenExceptionIsExpected() {
|
||||
Task task = new Task();
|
||||
task.setId(5);
|
||||
taskJpaRepository.persistAndFlush(task);
|
||||
|
||||
Task secondTask = new Task();
|
||||
secondTask.setId(5);
|
||||
|
||||
assertThrows(DataIntegrityViolationException.class,
|
||||
() -> taskJpaRepository.persistAndFlush(secondTask));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRepository_whenPersistNewTaskUsingNativeQuery_thenNoExtraSelectIsExpected() {
|
||||
Task task = new Task();
|
||||
task.setId(6);
|
||||
taskRepository.insert(task);
|
||||
|
||||
assertTrue(taskRepository.findById(6).isPresent());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
# spring.datasource.x
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=sa
|
||||
|
||||
# hibernate.X
|
||||
spring.jpa.hibernate.dialect=org.hibernate.dialect.H2Dialect
|
||||
spring.jpa.hibernate.ddl-auto=create-drop
|
||||
spring.jpa.hibernate.show_sql=true
|
||||
spring.jpa.hibernate.hbm2ddl.auto=create-drop
|
||||
spring.jpa.defer-datasource-initialization=true
|
Loading…
Reference in New Issue