persistence-with-springBoot
A. Annotations
- data.sql, schema.sql, JPA-buddy-PlugIn
@EnableJpaRepositories
("example.baeldung.com.repo")
@EntityScan
("example.baeldung.com.entity")
@EnableTransactionManagement
@Transactional
@Query
- if having indexed/named parameter then bind using @Param
on arg.
@Modifying
- method should run inside Transaction
- method returns - int,void
- annotation indicates that the method modifies the state of the database.
- testing : @Sql, @SqlConfig, @SqlGroup -
B. Multiple DataSource :
- DBP_01_Multiple Data Sources : https://chatgpt.com/c/7c2da8f5-3f44-4e71-b4f9-4cdadacf0ec5
- Create 2 beans - Ds1() and Ds2(), Apply ConfigurationProperties on method -> binds returnType(DataSource).
- create 2 beans - emF1 , emF2
- Create 2 beans - Txm1 , Txm2
- Create 2 sets of entities and repos:
- package1 : entities1, repos1, : use Datasource1
- package2 : entities2, repos2 : Use Datasource2. how, see below ?
@EnableJpaRepositories(
basePackages = "dao1/repos1/*",
entityManagerFactoryRef = "emF1",
transactionManagerRef = "txn1"
)
// same for another set
C. Spring JPA Data
- Purpose
- DAO/persistence layer usually consists of a lot of boilerplate code (CRUD methods, wire em, etc)
- SJD makes it possible to remove the DAO implementations entirely.
- find this interface and automatically create an implementation for it.
- Exception
- translation is still enabled by the use of the
@Repository
.
- All DB exception converts/translated to DataAccessException hierachy
- custom translator:
# 1
public class Custom_PersistenceExceptionTranslator implements PersistenceExceptionTranslator
{
@Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
...
}
}
# 2
@Configuration
Public class AppConfig {
@Bean
public PersistenceExceptionTranslator exceptionTranslator() {
return new Custom_PersistenceExceptionTranslator();
}}
Repository
1. CrudRepository<E,ID>
- returns
Iterable<E>
- ListCrudRepository --> returns
List<E>
<S extends T> S save(S entity);
T findOne(ID primaryKey);
Iterable<T> findAll();
Long count();
void delete(T entity);
boolean exists(ID primaryKey);
2. PagingAndSortingRepository<E,ID>
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
3. JpaRepository<E,ID>
4. Custom repo
- own interface and impl class
5. Composite repo
- mix of above (each is called
fragments
then). eg:
# 1
public interface ProductRepository extends CrudRepository<Product, Long> { }
# 2
public interface ProductPagingAndSortingRepository extends PagingAndSortingRepository<Product, Long> {}
# 3
public interface CustomProductRepository {
List<Product> findByCustomCriteria(String criteria);
}
public class CustomProductRepositoryImpl implements CustomProductRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Product> findByCustomCriteria(String criteria) {
String jpql = "SELECT p FROM Product p WHERE p.name LIKE :criteria";
return entityManager.createQuery(jpql, Product.class)
.setParameter("criteria", "%" + criteria + "%")
.getResultList();
}
}
Combine the base repositories and custom repository into a single repository interface. <<<
@Repository
public interface CompositeProductRepository extends
ProductRepository,
ProductPagingAndSortingRepository,
CustomProductRepository {
}
D. Queries
1. Derive methods
- methodName ::
<introducer> [distinct|Top|FIRST]+ "By" + <criteria> + "OrderBy"+propertyName () + Desc|Asc
introducer
: find, read, query, count and get
Criteria
::
- propertyName +
Is|IsNot
(T v)
- propertyName +
IsNull|IsNotNull
(v)
- booleanPropertyName +
True|False
()
- propertyName +
StartingWith
(String prefix)
- propertyName +
EndingWith
(String suffix)
- propertyName +
NameLike
(String likePattern);
- propertyName +
LessThan | LessThanEqual | GreaterThan | GreaterThanEqual | Between
(int...)
- propertyName +
In
(Collection c)
- propertyName +
After
(ZonedDateTime)
- propertyName +
Before
(ZonedDateTime)
List<User> findByNameIs(String name);
List<User> findByNameEquals(String name);
List<User> findByNameIsNot(String name);
List<User> findByNameIsNull();
List<User> findByNameIsNotNull();
List<User> findByNameStartingWith(String prefix);
List<User> findByNameStartingWith(String prefix);
List<User> findByNameEndingWith(String suffix);
List<User> findByNameContaining(String infix);
List<User> findByAgeLessThan(Integer age);
List<User> findByAgeLessThanEqual(Integer age);
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeGreaterThanEqual(Integer age);
List<User> findByAgeBetween(Integer startAge, Integer endAge);
List<User> findByAgeIn(Collection<Integer> ages);
List<User> findByBirthDateAfter(ZonedDateTime birthDate);
List<User> findByBirthDateBefore(ZonedDateTime birthDate);
- Grouping : And or (Not fan of it.)
- eg: List
findByNameOrAgeAndActiveCustom
(String name, Integer age, Boolean active);
- possibility-1 : (name = 'John' OR age = 30) AND active = true
- possibility-2 : name = 'John' OR (age = 30 AND active = true)
- its better to use @Query then, to take control.
@Query("SELECT u FROM User u WHERE u.name = ?1 OR (u.age = ?2 AND u.active = ?3)")
2. @Query
- notice:
- Sort
- Pageable
- Page
- PageRequest.of()
------------------------
// orderby / sorting
------------------------
sort-object-1:
- Sort.by("name")
- Sort.by("price").descending()
- Sort.by("price").descending().and(Sort.by("name"))
- Sort.by(Sort.Direction.ASC, "name")
@Query(value = "JPQL", nativeQuery = f)
- List<E> l = repo.findAll(sort-object-1);
------------------------
// pagination
------------------------
Pageable p = PageRequest.of(page_no, page_size, sort-object-1); // int, int, Sort
@Query(value = "JPQL", nativeQuery = f)
- Page<E> l = repo.findAll(Pageable pageable);
@Query(value = "SQL", nativeQuery = True, countQuery = '..count(*)..')
- Page<E> l = repo.findAll(Pageable pageable);
Pageable sortedByName =PageRequest.of(0, 3, Sort.by("name"));
Pageable sortedByPriceDesc = PageRequest.of(0, 3, Sort.by("price").descending());
Pageable sortedByPriceDescNameAsc = PageRequest.of(0, 5, Sort.by("price").descending().and(Sort.by("name")));
@Query(value = "SELECT u FROM User u WHERE u.name IN :names")
List<User> findUserByNameList(@Param("names") Collection<String> names);
- sample Page after json serialization:
{
"content": [
{
"id": 1,
"name": "John",
"email": "john@example.com"
}
],
"pageable": {
"pageNumber": 0,
"pageSize": 5
},
"totalPages": 10,
"totalElements": 50
}
More
- JPA Buddy plugin
- generation of JPA entities, Spring Data JPA repositories, DTOs,
- initialization DDL scripts,
- Flyway versioned migrations,
- provides an advanced tool for reverse engineering.