diff --git a/persistence-modules/spring-data-jpa-crud/README.md b/persistence-modules/spring-data-jpa-crud/README.md
new file mode 100644
index 0000000000..dc0c78c87e
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/README.md
@@ -0,0 +1,21 @@
+## Spring Data JPA - CRUD
+
+This module contains articles about CRUD operations in Spring Data JPA
+
+### Relevant Articles:
+- [Spring Data JPA – Derived Delete Methods](https://www.baeldung.com/spring-data-jpa-deleteby)
+- [Spring Data JPA Delete and Relationships](https://www.baeldung.com/spring-data-jpa-delete)
+- [INSERT Statement in JPA](https://www.baeldung.com/jpa-insert)
+- [Spring Data JPA Batch Inserts](https://www.baeldung.com/spring-data-jpa-batch-inserts)
+- [Batch Insert/Update with Hibernate/JPA](https://www.baeldung.com/jpa-hibernate-batch-insert-update)
+- [Difference Between save() and saveAndFlush() in Spring Data JPA](https://www.baeldung.com/spring-data-jpa-save-saveandflush)
+- [Generate Database Schema with Spring Data JPA](https://www.baeldung.com/spring-data-jpa-generate-db-schema)
+
+### Eclipse Config
+After importing the project into Eclipse, you may see the following error:
+"No persistence xml file found in project"
+
+This can be ignored:
+- Project -> Properties -> Java Persistance -> JPA -> Error/Warnings -> Select Ignore on "No persistence xml file found in project"
+Or:
+- Eclipse -> Preferences - Validation - disable the "Build" execution of the JPA Validator
diff --git a/persistence-modules/spring-data-jpa-crud/pom.xml b/persistence-modules/spring-data-jpa-crud/pom.xml
new file mode 100644
index 0000000000..1708d14fc2
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/pom.xml
@@ -0,0 +1,71 @@
+
+
+ 4.0.0
+ spring-data-jpa-crud
+ spring-data-jpa-crud
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+ com.h2database
+ h2
+
+
+ net.ttddyy
+ datasource-proxy
+ ${datasource-proxy.version}
+
+
+ org.postgresql
+ postgresql
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+
+ org.testcontainers
+ postgresql
+ ${testcontainers.version}
+ test
+
+
+
+
+
+ 1.4.1
+ 21.0
+ 1.12.2
+
+
+
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/Application.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/Application.java
new file mode 100644
index 0000000000..ce10072031
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/Application.java
@@ -0,0 +1,14 @@
+package com.baeldung;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+@SpringBootApplication
+@EnableJpaRepositories
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/DatasourceProxyBeanPostProcessor.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/DatasourceProxyBeanPostProcessor.java
new file mode 100644
index 0000000000..504357db44
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/DatasourceProxyBeanPostProcessor.java
@@ -0,0 +1,53 @@
+package com.baeldung.batchinserts;
+
+import java.lang.reflect.Method;
+import javax.sql.DataSource;
+import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ReflectionUtils;
+
+@Component
+@Profile("batchinserts")
+public class DatasourceProxyBeanPostProcessor implements BeanPostProcessor {
+
+ @Override
+ public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
+ if (bean instanceof DataSource) {
+ ProxyFactory factory = new ProxyFactory(bean);
+ factory.setProxyTargetClass(true);
+ factory.addAdvice(new ProxyDataSourceInterceptor((DataSource) bean));
+ return factory.getProxy();
+ }
+
+ return bean;
+ }
+
+ private static class ProxyDataSourceInterceptor implements MethodInterceptor {
+
+ private final DataSource dataSource;
+
+ public ProxyDataSourceInterceptor(final DataSource dataSource) {
+ this.dataSource = ProxyDataSourceBuilder.create(dataSource).name("Batch-Insert-Logger").asJson().countQuery().logQueryToSysOut().build();
+ }
+
+ @Override
+ public Object invoke(final MethodInvocation invocation) throws Throwable {
+ Method proxyMethod = ReflectionUtils.findMethod(dataSource.getClass(), invocation.getMethod().getName());
+ if (proxyMethod != null) {
+ return proxyMethod.invoke(dataSource, invocation.getArguments());
+ }
+ return invocation.proceed();
+ }
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/model/School.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/model/School.java
new file mode 100644
index 0000000000..6d2f333ac7
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/model/School.java
@@ -0,0 +1,45 @@
+package com.baeldung.batchinserts.model;
+
+import java.util.List;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+@Entity
+public class School {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
+ private long id;
+
+ private String name;
+
+ @OneToMany(mappedBy = "school")
+ private List students;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getStudents() {
+ return students;
+ }
+
+ public void setStudents(List students) {
+ this.students = students;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/model/Student.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/model/Student.java
new file mode 100644
index 0000000000..d38214f122
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/batchinserts/model/Student.java
@@ -0,0 +1,44 @@
+package com.baeldung.batchinserts.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class Student {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE)
+ private long id;
+
+ private String name;
+
+ @ManyToOne
+ private School school;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public School getSchool() {
+ return school;
+ }
+
+ public void setSchool(School school) {
+ this.school = school;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/Application.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/Application.java
new file mode 100644
index 0000000000..aaca760499
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/Application.java
@@ -0,0 +1,17 @@
+package com.baeldung.boot;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+@SpringBootApplication
+@EnableJpaRepositories("com.baeldung")
+@EntityScan("com.baeldung")
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/daos/CustomerRepository.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/daos/CustomerRepository.java
new file mode 100644
index 0000000000..7cb7e45b27
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/daos/CustomerRepository.java
@@ -0,0 +1,19 @@
+package com.baeldung.boot.daos;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baeldung.boot.domain.Customer;
+
+/**
+ * JPA CrudRepository interface
+ *
+ * @author ysharma2512
+ *
+ */
+@Repository
+@Transactional
+public interface CustomerRepository extends CrudRepository{
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/daos/impl/PersonInsertRepository.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/daos/impl/PersonInsertRepository.java
new file mode 100644
index 0000000000..373532e1c3
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/daos/impl/PersonInsertRepository.java
@@ -0,0 +1,31 @@
+package com.baeldung.boot.daos.impl;
+
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.boot.domain.Person;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.transaction.Transactional;
+
+@Repository
+public class PersonInsertRepository {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @Transactional
+ public void insertWithQuery(Person person) {
+ entityManager.createNativeQuery("INSERT INTO person (id, first_name, last_name) VALUES (?,?,?)")
+ .setParameter(1, person.getId())
+ .setParameter(2, person.getFirstName())
+ .setParameter(3, person.getLastName())
+ .executeUpdate();
+ }
+
+ @Transactional
+ public void insertWithEntityManager(Person person) {
+ this.entityManager.persist(person);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/domain/Customer.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/domain/Customer.java
new file mode 100644
index 0000000000..af88be0be6
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/domain/Customer.java
@@ -0,0 +1,56 @@
+package com.baeldung.boot.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+/**
+ * Customer Entity class
+ * @author ysharma2512
+ *
+ */
+@Entity
+public class Customer {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+ private String firstName;
+ private String lastName;
+
+ public Customer(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/domain/Person.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/domain/Person.java
new file mode 100644
index 0000000000..88894ccc72
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/domain/Person.java
@@ -0,0 +1,47 @@
+package com.baeldung.boot.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Person {
+
+ @Id
+ private Long id;
+ private String firstName;
+ private String lastName;
+
+ public Person() {
+ }
+
+ public Person(Long id, String firstName, String lastName) {
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/web/controllers/CustomerController.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/web/controllers/CustomerController.java
new file mode 100644
index 0000000000..e13afd9b45
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/boot/web/controllers/CustomerController.java
@@ -0,0 +1,41 @@
+package com.baeldung.boot.web.controllers;
+
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.boot.daos.CustomerRepository;
+import com.baeldung.boot.domain.Customer;
+
+/**
+ * A simple controller to test the JPA CrudRepository operations
+ * controllers
+ *
+ * @author ysharma2512
+ *
+ */
+@RestController
+public class CustomerController {
+
+ CustomerRepository customerRepository;
+
+ public CustomerController(CustomerRepository customerRepository2) {
+ this.customerRepository = customerRepository2;
+ }
+
+ @PostMapping("/customers")
+ public ResponseEntity> insertCustomers() throws URISyntaxException {
+ Customer c1 = new Customer("James", "Gosling");
+ Customer c2 = new Customer("Doug", "Lea");
+ Customer c3 = new Customer("Martin", "Fowler");
+ Customer c4 = new Customer("Brian", "Goetz");
+ List customers = Arrays.asList(c1, c2, c3, c4);
+ customerRepository.saveAll(customers);
+ return ResponseEntity.ok(customers);
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/entity/Book.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/entity/Book.java
new file mode 100644
index 0000000000..deac24548a
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/entity/Book.java
@@ -0,0 +1,51 @@
+package com.baeldung.datajpadelete.entity;
+
+import javax.persistence.*;
+
+@Entity
+public class Book {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+ private String title;
+
+ @ManyToOne
+ private Category category;
+
+ public Book() {
+ }
+
+ public Book(String title) {
+ this.title = title;
+ }
+
+ public Book(String title, Category category) {
+ this.title = title;
+ this.category = category;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public Category getCategory() {
+ return category;
+ }
+
+ public void setCategory(Category category) {
+ this.category = category;
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/entity/Category.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/entity/Category.java
new file mode 100644
index 0000000000..16f1a4157f
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/entity/Category.java
@@ -0,0 +1,60 @@
+package com.baeldung.datajpadelete.entity;
+
+import javax.persistence.*;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Entity
+public class Category {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+ private String name;
+
+ @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List books;
+
+ public Category() {
+ }
+
+ public Category(String name) {
+ this.name = name;
+ }
+
+ public Category(String name, Book... books) {
+ this.name = name;
+ this.books = Stream.of(books).collect(Collectors.toList());
+ this.books.forEach(x -> x.setCategory(this));
+ }
+
+ public Category(String name, List books) {
+ this.name = name;
+ this.books = books;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getBooks() {
+ return books;
+ }
+
+ public void setBooks(List books) {
+ this.books = books;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/repository/BookRepository.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/repository/BookRepository.java
new file mode 100644
index 0000000000..5d0f45f127
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/repository/BookRepository.java
@@ -0,0 +1,19 @@
+package com.baeldung.datajpadelete.repository;
+
+import com.baeldung.datajpadelete.entity.Book;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface BookRepository extends CrudRepository {
+
+ long deleteByTitle(String title);
+
+ @Modifying
+ @Query("delete from Book b where b.title=:title")
+ void deleteBooks(@Param("title") String title);
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/repository/CategoryRepository.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/repository/CategoryRepository.java
new file mode 100644
index 0000000000..6fe7058a78
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/datajpadelete/repository/CategoryRepository.java
@@ -0,0 +1,9 @@
+package com.baeldung.datajpadelete.repository;
+
+import com.baeldung.datajpadelete.entity.Category;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CategoryRepository extends CrudRepository {
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/entity/Employee.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/entity/Employee.java
new file mode 100644
index 0000000000..4c3dbd25b7
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/entity/Employee.java
@@ -0,0 +1,36 @@
+package com.baeldung.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Employee {
+
+ @Id
+ private Long id;
+ private String name;
+
+ public Employee() {
+ }
+
+ public Employee(Long id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/entity/Fruit.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/entity/Fruit.java
new file mode 100644
index 0000000000..d45ac33db8
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/entity/Fruit.java
@@ -0,0 +1,40 @@
+package com.baeldung.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+@Entity
+public class Fruit {
+
+ @Id
+ private long id;
+ private String name;
+ private String color;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/repository/EmployeeRepository.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/repository/EmployeeRepository.java
new file mode 100644
index 0000000000..8f0a80814b
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/repository/EmployeeRepository.java
@@ -0,0 +1,9 @@
+package com.baeldung.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.baeldung.entity.Employee;
+
+public interface EmployeeRepository extends JpaRepository {
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/repository/FruitRepository.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/repository/FruitRepository.java
new file mode 100644
index 0000000000..5055252adf
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/repository/FruitRepository.java
@@ -0,0 +1,27 @@
+package com.baeldung.repository;
+
+import java.util.List;
+
+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.entity.Fruit;
+
+@Repository
+public interface FruitRepository extends JpaRepository {
+
+ Long deleteByName(String name);
+
+ List deleteByColor(String color);
+
+ Long removeByName(String name);
+
+ List removeByColor(String color);
+
+ @Modifying
+ @Query("delete from Fruit f where f.name=:name or f.color=:color")
+ int deleteFruits(@Param("name") String name, @Param("color") String color);
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/AccountApplication.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/AccountApplication.java
new file mode 100644
index 0000000000..547992a6c1
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/AccountApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.schemageneration;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AccountApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AccountApplication.class, args);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/HibernateUtil.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/HibernateUtil.java
new file mode 100644
index 0000000000..7d69d65705
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/HibernateUtil.java
@@ -0,0 +1,39 @@
+package com.baeldung.schemageneration;
+
+import com.baeldung.schemageneration.model.Account;
+import com.baeldung.schemageneration.model.AccountSetting;
+import org.hibernate.boot.Metadata;
+import org.hibernate.boot.MetadataSources;
+import org.hibernate.boot.registry.StandardServiceRegistry;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+import org.hibernate.cfg.Environment;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+import org.hibernate.tool.schema.TargetType;
+
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+public class HibernateUtil {
+
+ /**
+ * Generates database create commands for the specified entities using Hibernate native API, SchemaExport.
+ * Creation commands are exported into the create.sql file.
+ */
+ public static void generateSchema() {
+ Map settings = new HashMap<>();
+ settings.put(Environment.URL, "jdbc:h2:mem:schema");
+
+ StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(settings).build();
+
+ MetadataSources metadataSources = new MetadataSources(serviceRegistry);
+ metadataSources.addAnnotatedClass(Account.class);
+ metadataSources.addAnnotatedClass(AccountSetting.class);
+ Metadata metadata = metadataSources.buildMetadata();
+
+ SchemaExport schemaExport = new SchemaExport();
+ schemaExport.setFormat(true);
+ schemaExport.setOutputFile("create.sql");
+ schemaExport.createOnly(EnumSet.of(TargetType.SCRIPT), metadata);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/model/Account.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/model/Account.java
new file mode 100644
index 0000000000..785e275e26
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/model/Account.java
@@ -0,0 +1,74 @@
+package com.baeldung.schemageneration.model;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = "accounts")
+public class Account {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+
+ @Column(nullable = false, length = 100)
+ private String name;
+
+ @Column(name = "email_address")
+ private String emailAddress;
+
+ @OneToMany(mappedBy = "account", cascade = CascadeType.ALL)
+ private List accountSettings = new ArrayList<>();
+
+ public Account() {
+ }
+
+ public Account(String name, String emailAddress) {
+ this.name = name;
+ this.emailAddress = emailAddress;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmailAddress() {
+ return emailAddress;
+ }
+
+ public void setEmailAddress(String emailAddress) {
+ this.emailAddress = emailAddress;
+ }
+
+ public List getAccountSettings() {
+ return accountSettings;
+ }
+
+ public void setAccountSettings(List accountSettings) {
+ this.accountSettings = accountSettings;
+ }
+
+ public void addAccountSetting(AccountSetting setting) {
+ this.accountSettings.add(setting);
+ setting.setAccount(this);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/model/AccountSetting.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/model/AccountSetting.java
new file mode 100644
index 0000000000..61e43894a8
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/model/AccountSetting.java
@@ -0,0 +1,68 @@
+package com.baeldung.schemageneration.model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "account_settings")
+public class AccountSetting {
+
+ @Id
+ @GeneratedValue
+ private Long id;
+
+ @Column(name = "name", nullable = false)
+ private String settingName;
+
+ @Column(name = "value", nullable = false)
+ private String settingValue;
+
+ @ManyToOne()
+ @JoinColumn(name ="account_id", nullable = false)
+ private Account account;
+
+ public AccountSetting() {
+ }
+
+ public AccountSetting(String settingName, String settingValue) {
+ this.settingName = settingName;
+ this.settingValue = settingValue;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getSettingName() {
+ return settingName;
+ }
+
+ public void setSettingName(String settingName) {
+ this.settingName = settingName;
+ }
+
+ public String getSettingValue() {
+ return settingValue;
+ }
+
+ public void setSettingValue(String settingValue) {
+ this.settingValue = settingValue;
+ }
+
+ public Account getAccount() {
+ return account;
+ }
+
+ public void setAccount(Account account) {
+ this.account = account;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/repository/AccountRepository.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/repository/AccountRepository.java
new file mode 100644
index 0000000000..dc57ffe6d3
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/repository/AccountRepository.java
@@ -0,0 +1,8 @@
+package com.baeldung.schemageneration.repository;
+
+import com.baeldung.schemageneration.model.Account;
+import org.springframework.data.repository.CrudRepository;
+
+public interface AccountRepository extends CrudRepository {
+ Account findByName(String name);
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/repository/AccountSettingRepository.java b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/repository/AccountSettingRepository.java
new file mode 100644
index 0000000000..c2b8ff7398
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/java/com/baeldung/schemageneration/repository/AccountSettingRepository.java
@@ -0,0 +1,8 @@
+package com.baeldung.schemageneration.repository;
+
+import com.baeldung.schemageneration.model.AccountSetting;
+import org.springframework.data.repository.CrudRepository;
+
+public interface AccountSettingRepository extends CrudRepository {
+ AccountSetting findByAccountId(Long accountId);
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties b/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties
new file mode 100644
index 0000000000..af0df308cd
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/resources/application.properties
@@ -0,0 +1,14 @@
+spring.main.allow-bean-definition-overriding=true
+
+spring.jpa.properties.hibernate.jdbc.batch_size=4
+spring.jpa.properties.hibernate.order_inserts=true
+spring.jpa.properties.hibernate.order_updates=true
+spring.jpa.properties.hibernate.generate_statistics=true
+
+# JPA-Schema-Generation
+# Use below configuration to generate database schema create commands based on the entity models
+# and export them into the create.sql file
+#spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
+#spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=create.sql
+#spring.jpa.properties.javax.persistence.schema-generation.scripts.create-source=metadata
+#spring.jpa.properties.hibernate.format_sql=true
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/main/resources/logback.xml b/persistence-modules/spring-data-jpa-crud/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/SpringContextTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/SpringContextTest.java
new file mode 100644
index 0000000000..eaccf4acba
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/SpringContextTest.java
@@ -0,0 +1,17 @@
+package com.baeldung;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.baeldung.boot.Application;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Application.class)
+public class SpringContextTest {
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/SpringJpaContextIntegrationTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/SpringJpaContextIntegrationTest.java
new file mode 100644
index 0000000000..27c71c5bcc
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/SpringJpaContextIntegrationTest.java
@@ -0,0 +1,19 @@
+package com.baeldung;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.baeldung.boot.Application;
+
+@RunWith(SpringRunner.class)
+@DataJpaTest
+@ContextConfiguration(classes = Application.class)
+public class SpringJpaContextIntegrationTest {
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java
new file mode 100644
index 0000000000..7ddf36d3f0
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/BatchInsertIntegrationTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.batchinserts;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import com.baeldung.boot.Application;
+import com.baeldung.boot.daos.CustomerRepository;
+import com.baeldung.boot.web.controllers.CustomerController;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes=Application.class)
+@AutoConfigureMockMvc
+public class BatchInsertIntegrationTest {
+
+ @Autowired
+ private CustomerRepository customerRepository;
+ private MockMvc mockMvc;
+ @Before
+ public void setUp() throws Exception {
+ mockMvc = MockMvcBuilders.standaloneSetup( new CustomerController(customerRepository))
+ .build();
+ }
+
+ @Test
+ public void whenInsertingCustomers_thenCustomersAreCreated() throws Exception {
+ this.mockMvc.perform(post("/customers"))
+ .andExpect(status().isOk());
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/JpaBatchInsertsIntegrationTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/JpaBatchInsertsIntegrationTest.java
new file mode 100644
index 0000000000..311f227322
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/JpaBatchInsertsIntegrationTest.java
@@ -0,0 +1,98 @@
+package com.baeldung.batchinserts;
+
+import static com.baeldung.batchinserts.TestObjectHelper.createSchool;
+import static com.baeldung.batchinserts.TestObjectHelper.createStudent;
+
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baeldung.batchinserts.model.School;
+import com.baeldung.batchinserts.model.Student;
+import com.baeldung.boot.Application;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Application.class)
+@Transactional
+@ActiveProfiles("batchinserts")
+public class JpaBatchInsertsIntegrationTest {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ private static final int BATCH_SIZE = 5;
+
+ @Transactional
+ @Test
+ public void whenInsertingSingleTypeOfEntity_thenCreatesSingleBatch() {
+ for (int i = 0; i < 10; i++) {
+ School school = createSchool(i);
+ entityManager.persist(school);
+ }
+ }
+
+ @Transactional
+ @Test
+ public void whenFlushingAfterBatch_ThenClearsMemory() {
+ for (int i = 0; i < 10; i++) {
+ if (i > 0 && i % BATCH_SIZE == 0) {
+ entityManager.flush();
+ entityManager.clear();
+ }
+
+ School school = createSchool(i);
+ entityManager.persist(school);
+ }
+ }
+
+ @Transactional
+ @Test
+ public void whenThereAreMultipleEntities_ThenCreatesNewBatch() {
+ for (int i = 0; i < 10; i++) {
+ if (i > 0 && i % BATCH_SIZE == 0) {
+ entityManager.flush();
+ entityManager.clear();
+ }
+
+ School school = createSchool(i);
+ entityManager.persist(school);
+ Student firstStudent = createStudent(school);
+ Student secondStudent = createStudent(school);
+ entityManager.persist(firstStudent);
+ entityManager.persist(secondStudent);
+ }
+ }
+
+ @Transactional
+ @Test
+ public void whenUpdatingEntities_thenCreatesBatch() {
+ for (int i = 0; i < 10; i++) {
+ School school = createSchool(i);
+ entityManager.persist(school);
+ }
+
+ entityManager.flush();
+
+ TypedQuery schoolQuery = entityManager.createQuery("SELECT s from School s", School.class);
+ List allSchools = schoolQuery.getResultList();
+
+ for (School school : allSchools) {
+ school.setName("Updated_" + school.getName());
+ }
+ }
+
+ @After
+ public void tearDown() {
+ entityManager.flush();
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/JpaNoBatchInsertsIntegrationTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/JpaNoBatchInsertsIntegrationTest.java
new file mode 100644
index 0000000000..75b3f1f3aa
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/JpaNoBatchInsertsIntegrationTest.java
@@ -0,0 +1,41 @@
+package com.baeldung.batchinserts;
+
+import static com.baeldung.batchinserts.TestObjectHelper.createSchool;
+
+import com.baeldung.batchinserts.model.School;
+import com.baeldung.boot.Application;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Application.class)
+@Transactional
+@ActiveProfiles("batchinserts")
+@TestPropertySource(properties = "spring.jpa.properties.hibernate.jdbc.batch_size=-1")
+public class JpaNoBatchInsertsIntegrationTest {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @Test
+ public void whenNotConfigured_ThenSendsInsertsSeparately() {
+ for (int i = 0; i < 10; i++) {
+ School school = createSchool(i);
+ entityManager.persist(school);
+ }
+ }
+
+ @After
+ public void tearDown() {
+ entityManager.flush();
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/TestObjectHelper.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/TestObjectHelper.java
new file mode 100644
index 0000000000..fcd26cb721
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/batchinserts/TestObjectHelper.java
@@ -0,0 +1,20 @@
+package com.baeldung.batchinserts;
+
+import com.baeldung.batchinserts.model.School;
+import com.baeldung.batchinserts.model.Student;
+
+public class TestObjectHelper {
+
+ public static School createSchool(int nameIdentifier) {
+ School school = new School();
+ school.setName("School" + (nameIdentifier + 1));
+ return school;
+ }
+
+ public static Student createStudent(School school) {
+ Student student = new Student();
+ student.setName("Student-" + school.getName());
+ student.setSchool(school);
+ return student;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/boot/daos/PersonInsertRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/boot/daos/PersonInsertRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..9d45c17035
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/boot/daos/PersonInsertRepositoryIntegrationTest.java
@@ -0,0 +1,82 @@
+package com.baeldung.boot.daos;
+
+import com.baeldung.boot.daos.impl.PersonInsertRepository;
+import com.baeldung.boot.domain.Person;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+@RunWith(SpringRunner.class)
+@DataJpaTest
+@Import(PersonInsertRepository.class)
+public class PersonInsertRepositoryIntegrationTest {
+
+ private static final Long ID = 1L;
+ private static final String FIRST_NAME = "firstname";
+ private static final String LAST_NAME = "lastname";
+ private static final Person PERSON = new Person(ID, FIRST_NAME, LAST_NAME);
+
+ @Autowired
+ private PersonInsertRepository personInsertRepository;
+
+ @Autowired
+ private EntityManager entityManager;
+
+ @Test
+ public void givenPersonEntity_whenInsertWithNativeQuery_ThenPersonIsPersisted() {
+ insertWithQuery();
+
+ assertPersonPersisted();
+ }
+
+ @Test
+ public void givenPersonEntity_whenInsertedTwiceWithNativeQuery_thenPersistenceExceptionExceptionIsThrown() {
+ assertThatExceptionOfType(PersistenceException.class).isThrownBy(() -> {
+ insertWithQuery();
+ insertWithQuery();
+ });
+ }
+
+ @Test
+ public void givenPersonEntity_whenInsertWithEntityManager_thenPersonIsPersisted() {
+ insertPersonWithEntityManager();
+
+ assertPersonPersisted();
+ }
+
+ @Test
+ public void givenPersonEntity_whenInsertedTwiceWithEntityManager_thenEntityExistsExceptionIsThrown() {
+ assertThatExceptionOfType(EntityExistsException.class).isThrownBy(() -> {
+ insertPersonWithEntityManager();
+ insertPersonWithEntityManager();
+ });
+ }
+
+ private void insertWithQuery() {
+ personInsertRepository.insertWithQuery(PERSON);
+ }
+
+ private void insertPersonWithEntityManager() {
+ personInsertRepository.insertWithEntityManager(new Person(ID, FIRST_NAME, LAST_NAME));
+ }
+
+ private void assertPersonPersisted() {
+ Person person = entityManager.find(Person.class, ID);
+
+ assertThat(person).isNotNull();
+ assertThat(person.getId()).isEqualTo(PERSON.getId());
+ assertThat(person.getFirstName()).isEqualTo(PERSON.getFirstName());
+ assertThat(person.getLastName()).isEqualTo(PERSON.getLastName());
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/datajpadelete/DeleteFromRepositoryUnitTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/datajpadelete/DeleteFromRepositoryUnitTest.java
new file mode 100644
index 0000000000..5f4a36bc0e
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/datajpadelete/DeleteFromRepositoryUnitTest.java
@@ -0,0 +1,72 @@
+package com.baeldung.datajpadelete;
+
+import com.baeldung.Application;
+import com.baeldung.datajpadelete.entity.Book;
+import com.baeldung.datajpadelete.repository.BookRepository;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {Application.class})
+public class DeleteFromRepositoryUnitTest {
+
+ @Autowired
+ private BookRepository repository;
+
+ Book book1;
+ Book book2;
+
+ @Before
+ public void setup() {
+ book1 = new Book("The Hobbit");
+ book2 = new Book("All Quiet on the Western Front");
+
+ repository.saveAll(Arrays.asList(book1, book2));
+ }
+
+ @After
+ public void teardown() {
+ repository.deleteAll();
+ }
+
+ @Test
+ public void whenDeleteByIdFromRepository_thenDeletingShouldBeSuccessful() {
+ repository.deleteById(book1.getId());
+
+ assertThat(repository.count()).isEqualTo(1);
+ }
+
+ @Test
+ public void whenDeleteAllFromRepository_thenRepositoryShouldBeEmpty() {
+ repository.deleteAll();
+
+ assertThat(repository.count()).isEqualTo(0);
+ }
+
+ @Test
+ @Transactional
+ public void whenDeleteFromDerivedQuery_thenDeletingShouldBeSuccessful() {
+ long deletedRecords = repository.deleteByTitle("The Hobbit");
+
+ assertThat(deletedRecords).isEqualTo(1);
+ }
+
+ @Test
+ @Transactional
+ public void whenDeleteFromCustomQuery_thenDeletingShouldBeSuccessful() {
+ repository.deleteBooks("The Hobbit");
+
+ assertThat(repository.count()).isEqualTo(1);
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/datajpadelete/DeleteInRelationshipsUnitTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/datajpadelete/DeleteInRelationshipsUnitTest.java
new file mode 100644
index 0000000000..6275ace6e0
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/datajpadelete/DeleteInRelationshipsUnitTest.java
@@ -0,0 +1,60 @@
+package com.baeldung.datajpadelete;
+
+import com.baeldung.Application;
+import com.baeldung.datajpadelete.entity.Book;
+import com.baeldung.datajpadelete.entity.Category;
+import com.baeldung.datajpadelete.repository.BookRepository;
+import com.baeldung.datajpadelete.repository.CategoryRepository;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {Application.class})
+public class DeleteInRelationshipsUnitTest {
+
+ @Autowired
+ private BookRepository bookRepository;
+
+ @Autowired
+ private CategoryRepository categoryRepository;
+
+ @Before
+ public void setup() {
+ Book book1 = new Book("The Hobbit");
+ Category category1 = new Category("Cat1", book1);
+ categoryRepository.save(category1);
+
+ Book book2 = new Book("All Quiet on the Western Front");
+ Category category2 = new Category("Cat2", book2);
+ categoryRepository.save(category2);
+ }
+
+ @After
+ public void teardown() {
+ bookRepository.deleteAll();
+ categoryRepository.deleteAll();
+ }
+
+ @Test
+ public void whenDeletingCategories_thenBooksShouldAlsoBeDeleted() {
+ categoryRepository.deleteAll();
+
+ assertThat(bookRepository.count()).isEqualTo(0);
+ assertThat(categoryRepository.count()).isEqualTo(0);
+ }
+
+ @Test
+ public void whenDeletingBooks_thenCategoriesShouldAlsoBeDeleted() {
+ bookRepository.deleteAll();
+
+ assertThat(bookRepository.count()).isEqualTo(0);
+ assertThat(categoryRepository.count()).isEqualTo(2);
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/repository/EmployeeRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/repository/EmployeeRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..0fc9918701
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/repository/EmployeeRepositoryIntegrationTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.repository;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.baeldung.boot.Application;
+import com.baeldung.entity.Employee;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Application.class)
+public class EmployeeRepositoryIntegrationTest {
+
+ private static final Employee EMPLOYEE1 = new Employee(1L, "John");
+ private static final Employee EMPLOYEE2 = new Employee(2L, "Alice");
+
+ @Autowired
+ private EmployeeRepository employeeRepository;
+
+ @Test
+ public void givenEmployeeEntity_whenInsertWithSave_ThenEmployeeIsPersisted() {
+ employeeRepository.save(EMPLOYEE1);
+ assertEmployeePersisted(EMPLOYEE1);
+ }
+
+ @Test
+ public void givenEmployeeEntity_whenInsertWithSaveAndFlush_ThenEmployeeIsPersisted() {
+ employeeRepository.saveAndFlush(EMPLOYEE2);
+ assertEmployeePersisted(EMPLOYEE2);
+ }
+
+ private void assertEmployeePersisted(Employee input) {
+ Employee employee = employeeRepository.getOne(input.getId());
+ assertThat(employee).isNotNull();
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/repository/FruitRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/repository/FruitRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..cf771dc833
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/repository/FruitRepositoryIntegrationTest.java
@@ -0,0 +1,75 @@
+package com.baeldung.repository;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baeldung.entity.Fruit;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+class FruitRepositoryIntegrationTest {
+
+ @Autowired
+ private FruitRepository fruitRepository;
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenDeletedByColor_ThenDeletedFruitsShouldReturn() {
+
+ List fruits = fruitRepository.deleteByColor("green");
+
+ assertEquals("number of fruits are not matching", 2, fruits.size());
+ fruits.forEach(fruit -> assertEquals("Its not a green fruit", "green", fruit.getColor()));
+ }
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenDeletedByName_ThenDeletedFruitCountShouldReturn() {
+
+ Long deletedFruitCount = fruitRepository.deleteByName("apple");
+
+ assertEquals("deleted fruit count is not matching", 1, deletedFruitCount.intValue());
+ }
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenRemovedByColor_ThenDeletedFruitsShouldReturn() {
+
+ List fruits = fruitRepository.removeByColor("green");
+
+ assertEquals("number of fruits are not matching", 2, fruits.size());
+ fruits.forEach(fruit -> assertEquals("Its not a green fruit", "green", fruit.getColor()));
+ }
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenRemovedByName_ThenDeletedFruitCountShouldReturn() {
+
+ Long deletedFruitCount = fruitRepository.removeByName("apple");
+
+ assertEquals("deleted fruit count is not matching", 1, deletedFruitCount.intValue());
+ }
+
+ @Transactional
+ @Test
+ @Sql(scripts = { "/test-fruit-data.sql" })
+ public void givenFruits_WhenDeletedByColorOrName_ThenDeletedFruitsShouldReturn() {
+
+ int deletedCount = fruitRepository.deleteFruits("apple", "green");
+
+ assertEquals("number of fruits are not matching", 3, deletedCount);
+ }
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/schemageneration/AccountRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/schemageneration/AccountRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..86a7671fe4
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/java/com/baeldung/schemageneration/AccountRepositoryIntegrationTest.java
@@ -0,0 +1,72 @@
+package com.baeldung.schemageneration;
+
+import com.baeldung.schemageneration.model.Account;
+import com.baeldung.schemageneration.model.AccountSetting;
+import com.baeldung.schemageneration.repository.AccountRepository;
+import com.baeldung.schemageneration.repository.AccountSettingRepository;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AccountApplication.class)
+public class AccountRepositoryIntegrationTest {
+
+ private static final String USER_NAME = "Eduard";
+ private static final String USER_EMAIL_ADDRESS = "eduard@gmx.com";
+ private static final String ACCOUNT_SETTING_NAME = "Timezone";
+ private static final String ACCOUNT_SETTING_VALUE = "UTC+02";
+
+ @Autowired
+ private AccountRepository accountRepository;
+
+ @Autowired
+ private AccountSettingRepository accountSettingRepository;
+
+ @After
+ public void tearDown() {
+ accountRepository.deleteAll();
+ }
+
+ @Test
+ public void givenNewAccount_whenSave_thenSuccess() {
+ Account account = new Account(USER_NAME, USER_EMAIL_ADDRESS);
+ accountRepository.save(account);
+
+ assertEquals(1, accountRepository.count());
+ }
+
+ @Test
+ public void givenSavedAccount_whenFindByName_thenFound() {
+ Account account = new Account(USER_NAME, USER_EMAIL_ADDRESS);
+ accountRepository.save(account);
+
+ Account accountFound = accountRepository.findByName(USER_NAME);
+
+ assertNotNull(accountFound);
+ assertEquals(USER_NAME, accountFound.getName());
+ assertEquals(USER_EMAIL_ADDRESS, accountFound.getEmailAddress());
+ }
+
+ @Test
+ public void givenSavedAccount_whenAccountSettingIsAdded_thenPersisted() {
+ Account account = new Account(USER_NAME, USER_EMAIL_ADDRESS);
+ account.addAccountSetting(new AccountSetting(ACCOUNT_SETTING_NAME, ACCOUNT_SETTING_VALUE));
+ accountRepository.save(account);
+
+ Account accountFound = accountRepository.findByName(USER_NAME);
+ assertNotNull(accountFound);
+ AccountSetting accountSetting = accountSettingRepository.findByAccountId(accountFound.getId());
+
+ assertNotNull(accountSetting);
+ assertEquals(ACCOUNT_SETTING_NAME, accountSetting.getSettingName());
+ assertEquals(ACCOUNT_SETTING_VALUE, accountSetting.getSettingValue());
+ }
+
+}
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/resources/application-batchinserts.properties b/persistence-modules/spring-data-jpa-crud/src/test/resources/application-batchinserts.properties
new file mode 100644
index 0000000000..4141f5668e
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/resources/application-batchinserts.properties
@@ -0,0 +1,6 @@
+spring.jpa.show-sql=false
+
+spring.jpa.properties.hibernate.jdbc.batch_size=5
+spring.jpa.properties.hibernate.order_inserts=true
+spring.jpa.properties.hibernate.order_updates=true
+spring.jpa.properties.hibernate.batch_versioned_data=true
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/resources/application-test.properties b/persistence-modules/spring-data-jpa-crud/src/test/resources/application-test.properties
new file mode 100644
index 0000000000..f9497c8f37
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/resources/application-test.properties
@@ -0,0 +1,2 @@
+spring.jpa.hibernate.ddl-auto=update
+spring.datasource.url=jdbc:h2:mem:jpa3
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-crud/src/test/resources/test-fruit-data.sql b/persistence-modules/spring-data-jpa-crud/src/test/resources/test-fruit-data.sql
new file mode 100644
index 0000000000..d99f42e5a7
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-crud/src/test/resources/test-fruit-data.sql
@@ -0,0 +1,6 @@
+truncate table fruit;
+
+insert into fruit(id,name,color) values (1,'apple','red');
+insert into fruit(id,name,color) values (2,'custard apple','green');
+insert into fruit(id,name,color) values (3,'mango','yellow');
+insert into fruit(id,name,color) values (4,'guava','green');
\ No newline at end of file