Starter
Spring Data JPA provides repository support for the Java Persistence API (JPA). It eases development of applications that need to access JPA data sources.
JPA : 一套规范(标准接口),而Hibernate,TopLink,JDO等是JPA的一种实现产品。
Spring Data JPA : 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架(底层默认实现使用的是Hibernate)。
Repository : Spring Data 的核心接口,是一个空接口,也叫标记接口(不包含任何方法声明)
public interface Repository<T,ID extends Serializable> {}
- 使用:
- 方式一:自定义一个接口
extends Repository
,则这个接口会被Spring容器所管理 - 方式二: 使用注解方式:
@RespositoryDefinition(domainClass=T.class,idClass=ID.class)
- 方式一:自定义一个接口
- 使用:
使用
查询
规范方法名实现查询
findBy...
,不需要写实现(弊端:方法名较长,无法实现复杂查询)//select * from Employee where name =? Employee findByName(String name); // where name like ?% and age <? public List<Employee> findByNameStartingWithAndAgeLessThan(String name, Integer age); // where name like %? and age <? public List<Employee> findByNameEndingWithAndAgeLessThan(String name, Integer age); // where name in (?,?....) or age <? public List<Employee> findByNameInOrAgeLessThan(List<String> names, Integer age); // where name in (?,?....) and age <? public List<Employee> findByNameInAndAgeLessThan(List<String> names, Integer age);
使用
@Query(HQL/JPQL/NativeSQL)
注解 (加在方法上,方法名称随意,支持命名参数和索引参数的使用)@Query("select o from Employee o where id=(select max(id) from Employee t1)") public Employee getEmployeeByMaxId(); @Query("select o from Employee o where o.name=?1 and o.age=?2") public List<Employee> queryParams1(String name, Integer age); @Query("select o from Employee o where o.name=:name and o.age=:age") public List<Employee> queryParams2(@Param("name")String name, @Param("age")Integer age); @Query("select o from Employee o where o.name like %?1%") public List<Employee> queryLike1(String name); @Query("select o from Employee o where o.name like %:name%") public List<Employee> queryLike2(@Param("name")String name); @Query(nativeQuery = true, value = "select count(1) from employee") public long getCount();
Keyword | Sample | JPQL Snippet |
---|---|---|
And | findByLastnameAndFirstname | ... where x.lastname=?1 and x.firstname=?2 |
Or | findByLastnameOrFirstname | ... where x.lastname=?1 or x.firstname=?2 |
Between | findByStartDateBetween | ... where x.startDate between ?1 and ?2 |
LessThan | findByAgeGreaterThan | ... where x.age<?1 |
GreaterThan | findByAgeGreaterThan | ... where x.age>?1 |
After | findByStartDateAfter | ... where x.startDate>?1 |
Before | findByStartDateBefore | ... where x.startDate<?1 |
IsNull | findByAgeIsNull | ... where x.age is null |
IsNotNull,NotNull | findByAgeIsNotNull | ... where x.age is not null |
Like | findByFirstnameLike | ... where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | ... where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | ... where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | ... where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | ... where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | ... where x.age=? order by x.lastname desc |
Not | findByLastnameNot | ... where x.lastname <> ?1 |
In | findByAgeIn(Collection |
... where x.age in ?1 |
NotIn | findByAgeNotIn(Collection |
... where x.age not in ?1 |
TRUE | findByActiveTrue() | ... where x.active=true |
FALSE | findByActiveFalse() | ... where x.active=false |
更新/删除
- Repository层: 方法上添加注解
@Modifying
- Service层: 方法上添加事务注解
@Transactional
Sample:
EmployeeRepository
@Modifying @Query("update Employee o set o.age = :age where o.id = :id") public void updateById(@Param("id")Integer id, @Param("age")Integer age); @Query("delete from Employee where id=?1") @Modifying public int deleteOneById(Integer id);
EmployeeService
@Transactional public boolean update(Integer id,Integer age) { return this.employeeRepository.updateById(id,age); } @Transactional public boolean delete(Integer id) { return this.employeeRepository.deleteOneById(id); }
Repository 子接口
CrudRepository extends Repository
: 包含CRUD方法// save <S extends T> S save(S entity); <S extends T> Iterable<S> save(Iterable<S> entities); // find Optional<T> findById(ID id); boolean existsById(ID id); Iterable<T> findAll(); Iterable<T> findAllById(Iterable<ID> ids); // delete void deleteById(ID id); void delete(T entity); void deleteAll(Iterable<? extends T> entities); void deleteAll(); long count();
PagingAndSortingRepository extends CrudRepository
: 包含分页排序方法Iterable<T> findAll(Sort sort) // 带排序的查询 Page<T> findAll(Pageable pageable) // 带排序的分页查询
JpaRepository extends PagingAndSortingRepository
: 包含Jpa规范的方法// save <S extends T> S saveAndFlush(S entity); <S extends T> List<S> saveAll(Iterable<S> entities); // flush void flush(); // find T getOne(ID id); List<T> findAll(); List<T> findAll(Sort sort); List<T> findAllById(Iterable<ID> ids); <S extends T> List<S> findAll(Example<S> example); <S extends T> List<S> findAll(Example<S> example, Sort sort); // delete void deleteInBatch(Iterable<T> entities); void deleteAllInBatch();
sample:
- Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> { }
Test
public void listByPageAndSortTest(){ Sort sort=Sort.by(Order.desc("name"),Order.asc("id")); Pageable pageable=PageRequest.of(0, 10,sort); Page<Employee> page=employeeRepository.findAll(pageable); System.out.println("Total Records:"+page.getTotalElements()); System.out.println("Total Pages:"+page.getTotalPages()); System.out.println("Current Page:"+(page.getNumber()+1)); System.out.println("Current Records:"+page.getNumberOfElements()); System.out.println("Limit:"+page.getSize()); System.out.println("Sort:"+page.getSort()); List<Employee> list=page.getContent(); for(Employee e:list) { System.out.println(e.getId()+":"+e.getName()+" DepartmentId:"+e.getDepartment().getId()); } System.out.println("Record Size:"+list.size()); }
JpaSpecificationExecutor 接口
封装JPA Criteria
查询
public interface JpaSpecificationExecutor<T> {
Optional<T> findOne(@Nullable Specification<T> spec);
List<T> findAll(@Nullable Specification<T> spec);
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
List<T> findAll(@Nullable Specification<T> spec, Sort sort);
long count(@Nullable Specification<T> spec);
}
Specification<T> spec=new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
return null;
}
};
root
: 可从query.from
获取,常用方法:root.join
,root.fetch
,root.get
query
: 可从criteriaBuilder.createQuery
,criteriaBuilder.createTupleQuery
获取query.select
(returnquery
)query.from
(returnroot
)query.where
(returnquery
)
criteriaBuilder
:- 可从
EntityManager
或EntityManagerFactory
类(em.getCriteriaBuilder()
)中获得criteriaBuilder对象 - 通过调用它的条件方法(equal,notEqual, gt, ge,lt, le,between,like等)和逻辑方法(and,or,not)创建
Predicate
对象
- 可从
Sample:
- Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer>,JpaSpecificationExecutor<Employee> { }
Test
public void listBySpecificationTest(){ Specification<Employee> spec=new Specification<Employee>() { @Override public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { Predicate p1=criteriaBuilder.like(root.get("name"), "Test%"); Predicate p2=criteriaBuilder.gt(root.get("age"), 18); //return criteriaBuilder.and(p1,p2); query.where(criteriaBuilder.and(p1,p2)) .orderBy(criteriaBuilder.desc(root.get("id"))); return query.getRestriction(); } }; List<Employee> list=employeeRepository.findAll(specification); for(Employee e:list) { System.out.println(e); } }
Demo
- Table (
@Table
):- pe_employee : id,name,remark,department_id
- pe_department : id,name,remark
- Entity (
@Entity
):- Employee : id,name,remark,Department department
- Department : id,name,remark,List
employees
- Relationship (
@ManyToOne
,@OneToMany
):- Employee -> Department : ManyToOne
- Employee <- Department : OneToMany
Entity
Employee:
package com.cj.demo.entity; import static javax.persistence.GenerationType.IDENTITY; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "pe_employee", catalog = "demo1") //@DynamicUpdate public class Employee { private Integer id; private String name; private Department department; private String remark; @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToOne(fetch=FetchType.LAZY) @JoinColumn(name = "department_id", nullable = false) public Department getDepartment() { return department; } public void setDepartment(Department department) { this.department = department; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", department=" + department + ", remark=" + remark + "]"; } }
Department:
package com.cj.demo.entity; import static javax.persistence.GenerationType.IDENTITY; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name = "pe_department", catalog = "demo1") public class Department { private Integer id; private String name; private String remark; private List<Employee> employees=new ArrayList<Employee>(); public Department() { } public Department(Integer id) { this.id=id; } @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } @OneToMany(fetch=FetchType.LAZY,mappedBy="department") public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } @Override public String toString() { return "Department [id=" + id + ", name=" + name + ", remark=" + remark + ", employees=" + employees + "]"; } }
Test Structure
Repository:
public interface EmployeeRepository extends JpaRepository<Employee, Integer>,JpaSpecificationExecutor<Employee> { // ... }
Service:
@Service public class EmployeeService { @Autowired private EmployeeRepository employeeRepository; // call repository function }
Test:
@RunWith(SpringRunner.class) @SpringBootTest public class EmployeeServiceTest { @Autowired private EmployeeService employeeService; @Test public void queryTest() { //... } }
Query
interface exist function:
T getOne(ID id)
- 懒加载(lazyLoad), 返回一个代理对象
E_$$_jvstxxx
(获取对象属性值时再触发执行query) - 当没有找到匹配记录时,抛出
EntityNotFoundException
- 懒加载(lazyLoad), 返回一个代理对象
Optional<T> findById(ID id)
/List<T> findAll()
- 马上触发执行query
- 当没有找到匹配记录时,不会抛出EntityNotFoundException
named query function:
public List<Employee> findByDepartmentId(Integer id); public List<Employee> findByDepartment(Department department); // query: // select p from Employee p left join p.department c where c.id=? // return: // List<Employee> // [ Employee: id,name,remark,department - proxy: `department_$$_xxxx (id)`] // Note: // `findByDepartment`只会根据department的key(id)来查询,department对象的其它属性(eg:name)不会用到
JPQL(
@Query
)/* 1 */ @Query("from Employee e where e.department.id=?1") public List<Employee> list(Integer departmentId); @Query("from Employee e where e.department.id=?1") public Page<Employee> listByPage(Integer departmentId,Pageable pageable); // query: // select e from Employee e where e.department.id=?1 // countQuery: // select count(id) from Employee e where e.department.id=?1 // Note: The JPQL should use alias for tables. // + `from Employee where department.id=?1`: wrong! // + `from Employee e where e.department.id=?1`: success! // + `from Employee where name like ?1`: ok! /* 2. Employee-Department(ManyToOne) */ @Query("from Employee e left join e.department where e.department.id=?1") public List<Employee listWithDepartment(Integer departmentId); // use `left join` // return : `e` & `e.department` // => store in Employee: id,name,remark,department(id,name,remark,employees:empty) // Note: // + if use `select e from ...`,won't get department // + can't use `select * from ...` // + for ManyToOne and no Pageable, `left join` is enough,`fetch` is not necessary. @Query("from Employee e left join e.department where e.department.id=?1") public Page<Object[]> listWithDepartmentByPage(Integer departmentId,Pageable pageable); // query: // select e,e.department from Employee e left join e.department where e.department.id=?1 // countQuery: // select count(e) from Employee e left join e.department where e.department.id=?1 // return : `e` & `e.department` // + Pageable: content won't store in Employee, get two objects List: `List<Object[]>` // + no Pageable: content will store in Employee,get one object List: `List<Employee>` // Note: // if use `select e from ...`,won't get department // can't use `select * from ...` @Query( value="from Employee e left join fetch e.department where e.department.id=?1", countQuery="select count(e) from Employee e left join e.department where e.department.id=?1" //countQuery="from Employee e where e.department.id=?1" ) // countQuery can't use fetch !! public Page<Employee> listFetchWithDepartmentByPage(Integer departmentId,Pageable pageable); // use `left join fetch` // query: // select e,e.department from Employee e left join fetch e.department where e.department.id=?1 // countQuery: // select count(e) from Employee e left join e.department where e.department.id=?1 // 也可使用`select count(e) from Employee e where e.department.id=?1` // return : `e` & `e.department` // => store in Employee,get one object List:`List<Employee>` // Note: // can't use `fetch` for countQuery // or will throw org.hibernate.QueryException (query specified join fetching, but the owner of the fetched association was not present in the select list ) /* 3. Department-Employee (OneToMany) */ @Query("select distinct p from Department p left join fetch p.employees") public List<Department> list() // query: // select * from department left outer join employee; // return: p & p.employee // => store in Department(包含Employee对象): id,name,remark,employees // Note: // for OneToMany, use `select distinct p` for root
Specification (extends
JpaSpecificationExecutor<T>
)Employee(Department): ManyToOne
criteriaBuilder.equal(root.get("department").get("id"), 1); // Note: can't use `root.get("department.id")` // select * from Employee where department.id=? and name like ?; criteriaBuilder.equal(root.get("department").get("name"), "QA"); // will auto `inner join` Department // select * from Employee e join e.department where department.name=? and name like ? criteriaBuilder.equal(root.join("department",JoinType.LEFT).get("name"),"QA"); // will `left join` Department // select * from Employee e left join e.department where department.name=? and name like ?; root.fetch("department",JoinType.LEFT); criteriaBuilder.like(root.get("name"), "Test%") ... // will `fetch` Department into Employee // select * from Employee e left join fetch e.department where e.name like ? // Note: // if use Pageable, countQuery can't use fetch, need to use the `CriteriaQuery.getResultType()` to check whether the query is projection or not /* Sample: */ Specification<Employee> spec=new Specification<Employee>() { @Override public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { // to check whether it's a count query if(!Long.class.equals(query.getResultType())){ root.fetch("department",JoinType.LEFT); } Predicate p1=criteriaBuilder.equal(root.get("department").get("id"), 1); Predicate p2=criteriaBuilder.like(root.get("name"), "Test%"); query.where(criteriaBuilder.and(p1,p2)) .orderBy(criteriaBuilder.desc(root.get("id"))); return query.getRestriction(); } };
Department(Employee): OneToMany
/* 1. need to use 'join' to set Many's conditions. criteriaBuilder.like(root.get("employees").get("name"), "Test%"); //error 2. If want to fetch Many: - Method1: root.fetch,then cast to join - Method2: use @EntityGraph + root.join 3. Note: won't limit records,and the total records are not correct for `Pageable`. */ Specification<Department> spec=new Specification<Department>() { @Override public Predicate toPredicate(Root<Department> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { Predicate p1=null; // to check whether it's a count query -- no need if used @EntityGraph if(!Long.class.equals(query.getResultType()) && !long.class.equals(query.getResultType())){ // fetch many: use root.fetch,then cast to join Fetch<Department,Employee> ep=root.fetch("employees",JoinType.LEFT); Join<Department,Employee> join=(Join<Department,Employee>)ep; p1=criteriaBuilder.like(join.get("name"), "Emp%"); }else{ p1=criteriaBuilder.like(root.join("employees",JoinType.LEFT).get("name"), "Test%"); } Predicate p2=criteriaBuilder.like(root.get("name"), "Dep%"); query.where(criteriaBuilder.and(p1,p2)) .orderBy(criteriaBuilder.desc(root.get("id"))); return query.getRestriction(); } }; /* Use @EntityGraph Sample: */ // In DepartmentRepository: @EntityGraph(attributePaths="employees") public Page<Department> findAll(Specification<Department> spec,Pageable pageable); // Specification: Specification<Department> spec=new Specification<Department>() { @Override public Predicate toPredicate(Root<Department> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { Join<Department,Employee> join=root.join("employees",JoinType.LEFT); Predicate p = criteriaBuilder.isNull(join); Predicate p1=criteriaBuilder.like(join.get("name"), "Test%"); Predicate p2=criteriaBuilder.like(root.get("name"), "QA%"); query.where(criteriaBuilder.and(criteriaBuilder.or(p,p1),p2)) .orderBy(criteriaBuilder.desc(root.get("id"))); return query.getRestriction(); } };
@EntityGraph
Employee-Department(ManyToOne)
/* 1. use @Query */ @EntityGraph(attributePaths="department"/*,type = EntityGraph.EntityGraphType.FETCH*/) @Query("from Employee e") public Page<Employee> listByGraphAndPage(Pageable pageable); // query: // select * from Employee left join e.department // countQuery: // select count(e.id) from Employee // return: // e & e.department stored in Employee // (will get correct Pagable records) /* 2. use Specification */ @EntityGraph(attributePaths="department") public Page<Employee> findAll(Specification<Employee> spec,Pageable pageable); // Test // Note: // + no need to set fetch manually // + Specification中不需要再使用`CriteriaQuery.getResultType()`来检查区分是否是countQuery了 Specification<Employee> spec=new Specification<Employee>() { @Override public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { // to check whether it's a count query // if(!Long.class.equals(query.getResultType())){ // root.fetch("department",JoinType.LEFT); // } Predicate p1=criteriaBuilder.equal(root.get("department").get("id"), 1); Predicate p2=criteriaBuilder.like(root.get("name"), "Test%"); query.where(criteriaBuilder.and(p1,p2)) .orderBy(criteriaBuilder.desc(root.get("id"))); return query.getRestriction(); } }; Sort sort=Sort.by(Order.desc("name"),Order.asc("id")); Pageable pageable=PageRequest.of(0, 3,sort); Page<Employee> page=this.employeeService.listAll(spec,pageable);
- Department-Employee(OneToMany)
@EntityGraph(attributePaths="employees") @Query("from Department p left join p.employees e where e.name like ?1") public Page<Department> listByGraphAndPage(String empName,Pageable pageable); // query: // select * from Department p left join p.employees e where e.name like ?1 // countQuery: // select count(e.id) from Department p left join p.employees e where e.name like ?1 // return: // p & p.employees stored in Department // (will get correct Pagable records)
T Save(T e)
- e: a transient object:
- no primaryKey: do
insert
- if associate obj(eg: Department) is not exist,will throw Exception when do insert.
- return saved obj(同save时传入的对象),eg: Employee: id,name,remark,department(id,name:null,remark:null,employees:empty)
- has PrimaryKey: do
select
-> doinsert
/update
/nothing
:- not exist => do
insert
- exist & different => do
update
(update all columnes) - exist & no different => do
nothing
- return : selected persist object(不同save时传入的对象). eg: Employee: id,name,remark,department - proxy : department_$$_xxxx (id)
- not exist => do
- no primaryKey: do
- e: a persist object: do
update
/nothing
(wont't executeselect
at first)- has change => do
update
, return the updated persist obj - no change => do
nothing
, return : the persist obj
- has change => do
- Note: For Hibernate implentation, if add
@DynamicUpdate
on the entity,theupdate
would be dynamic,just update the changed column value
Summary:
- Create:
- a transient object & no primaryKey
- a transient object & a unused primaryKey (will do select first)
- Update:
- a transient object & a exist obj's primaryKey & different (will do select first)
- a persist object & has change
Create/Update/Delete
Create:
T save(T e)
:- a transient object & no primaryKey
- a transient object & a unused primaryKey (will do select first)
Update:
T save(T e)
:- a transient object & a exist obj's primaryKey & different (will do select first)
- a persist object & has change
@Modifying
+@Query("update ...")
Note: only use
void
orint
/Integer
as return type/* interface Repository : */ @Query("update Employee set name=?2 where id=?1") @Modifying public int updateName(Integer id,String newName); /* class Service : */ @Transactional public int updateName(Integer id,String newName){ this.employeeRepository.updateName(id,newName); }
Delete:
- Exist function:
void delete(T entity)
: doselect
by Id -> dodelete
/insert
void delete(ID id)
: doselect
by Id -> dodelete
/ throw EmptyResultDataAccessException- Note: no return
- Named function:
- deleteByXxx,removeByXxxx
- Process:
select
all ids, then dodelete
one by one - Note: could return int/Integer/removed entities
public List<Employee> deleteByDepartmentId(Integer departmentId); public List<Employee> removeByDepartmentId(Integer departmentId); // Step1. `select` all ids: // select e from Employee e left join e.department d where d.id=? // => employee ids // Step2. do `delete` one by one: // delete from Employee where id=? // delete from Employee where id=? // ...
JPQL function:
@Modifiing
+@Query("delete from ...")
Note: could return void/int/Integer(changed row count)
/* interface Repository : */ @Query("delete from Employee where department.id=?1") @Modifying public int deleteDirectlyByDepartmentId(Integer departmentId); /* class Service : */ @Transactional public int delete(Integer departmentId){ this.employeeRepository.deleteDirectlyByDepartmentId(departmentId); }
- Exist function:
Custom Repository
interface EmployeeRepositoryCustom:
public interface EmployeeRepositoryCustom<T,ID> { public boolean update(T entity,String...properties); public int delete(T entity,String...properties); }
interface EmployeeRepository (extends EmployeeRepositoryCustom ) :
public interface EmployeeRepository extends JpaRepository<Employee, Integer>,EmployeeRepositoryCustom<Employee, Integer> { }
class EmployeeRepositoryImpl (implements EmployeeRepositoryCustom):
public class EmployeeRepositoryImpl implements EmployeeRepositoryCustom<Employee,Integer>{ @PersistenceContext private EntityManager em; @Override public boolean update(Employee entity, String... properties) { System.out.println("Update dynamic...."); if(properties==null || properties.length==0) return false; try { EntityManagerFactory factory=em.getEntityManagerFactory(); Object idValue=factory.getPersistenceUnitUtil().getIdentifier(entity); if(idValue==null) return false; Metamodel metamodel=factory.getMetamodel(); EntityType<? extends Object> entityType=metamodel.entity(entity.getClass()); String entityName = entityType.getJavaType().getSimpleName(); String idProperty=entityType.getId(Integer.class).getName(); String hql="Update "+entityName+" t set "; for(String property:properties) { if(property!=null && properties.length!=0) hql+="t."+property+"=?,"; } hql=hql.substring(0,hql.length()-1)+" where t."+idProperty+"=?"; Query query=em.createQuery(hql); for(int i=0;i<properties.length;i++) { String property=properties[i]; query.setParameter(i, PropertyUtils.getProperty(entity, property)); } query.setParameter(properties.length, idValue); return query.executeUpdate()>0; }catch(Exception ex) { System.out.println(ex.getMessage()); return false; } } @Override public int delete(Employee entity, String... properties) { System.out.println("Delete dynamic...."); if(properties==null || properties.length==0) return 0; try { EntityManagerFactory factory=em.getEntityManagerFactory(); Metamodel metamodel=factory.getMetamodel(); EntityType<? extends Object> entityType=metamodel.entity(entity.getClass()); String entityName = entityType.getJavaType().getSimpleName(); String hql="delete from "+entityName+" t where "; for(String property:properties) { if(property!=null && properties.length!=0) hql+="t."+property+"=? and "; } hql=hql+" 1=1"; Query query=em.createQuery(hql); for(int i=0;i<properties.length;i++) { String property=properties[i]; query.setParameter(i, PropertyUtils.getProperty(entity, property)); } return query.executeUpdate(); }catch(Exception ex) { System.out.println(ex.getMessage()); return 0; } } }
Service:
@Service public class EmployeeService { @Autowired private EmployeeRepository employeeRepository; // Update: Dynamic: em + HQL @Transactional public boolean updateDynamic(Employee e,String...properties) { return this.employeeRepository.update(e, properties); } // Delete: Dynamic: em + HQL @Transactional public int deleteDynamic(Employee e,String...properties) { return this.employeeRepository.delete(e, properties); } }
Test:
/* * Update by Custom Implementation * * Use em and build dynamic "update" hql by properties * ( No need add @Modify on Repository) * * */ @Test public void updateDynamicByHQLTest() { Employee e = new Employee(); e.setId(3); e.setName("Hello-Dynamic4"); e.setDepartment(new Department(1)); boolean result=this.employeeService.updateDynamic(e, "name"); System.out.println(result); } /* * Delete by Custom Implementation * * Use em and build dynamic "delete" hql by properties * ( No need add @Modify on Repository) * * */ @Test public void deleteDynamicTest() { Employee e = new Employee(); e.setId(6); e.setName("Test"); int result=this.employeeService.deleteDynamic(e,"id","name"); System.out.println(result); }