JPA

这些操作完全不用我们去实现,这些不是我们以往在普通的Spring项目中自己所定义的BaseDao吗?SpringBoot真的是非常体贴,大大减低了我们的工作量。但是更为强大的还在后面。我们通过继承JpaRepository接口,除了可以获得上面的基础CRUD操作方法之外,还可以通过Spring规定的接口命名方法自动创建复杂的CRUD操作,以下是我在Spring Data JPA 文档中找到的命名规则表:

 

KeywordSampleJPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… 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

findByAge(Is)NotNull

… where x.age 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 = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> age)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

在当然@Query不止是查询语言也可以用于更新和删除语句,不过需要额外添加@Modifying annotation,由于实验环境我就直接打上@Transactional声明式事务,在正式的开发环境请在你的业务层上写@Transactional,当然你也可以都写上,这样会按照你配置的或者是默认的事务传播机制进行事务传播:

 

@Modifying
@Transactional(readOnly = false)
@Query("update Weibo w set w.weiboText = :text where w.user = :user")
int setUserWeiboContent(@Param("text")String weiboText,@Param("user")User user);

 

 

除了@Query annotation 还可以在Entity类中使用@NamedQuery,定义查询语句。但是笔者觉得还是不及@Query来得直接方便。

 

@Entity
@Table(name = "users")
@NamedQuery(name = "User.searchUserName",query = "select u from User u where u.username like :username")
public class User {

UserRepository接口定义searchUserName方法即可:

 

 

public interface UserRepository extends JpaRepository<User,Long> {

    List<User> searchUserName(@Param("username") String username);

 

 

四、JpaSpecificationExecutor接口

还记得刚刚暂时忽略的JpaSpecificationExecutor<Weibo>接口继承吗?在WeiboRepository接口中继承JpaSpecificationExecutor<Weibo>接口就可以在外部使用Spring提供的Criteria查询,但是这个查询也存在一些问题~  以下就是一段范例:

 

@RequestMapping("/searchWeibo")
    public Page<Weibo> searchWeibo(final String username, final String weiboText, final Date startDate, final Date endDate,int pageNo,int pageSize) {
        Page<Weibo> page = this.weiboRepository.findAll(new Specification<Weibo>() {
            @Override
            public Predicate toPredicate(Root<Weibo> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new LinkedList<>();

                if (!StringUtils.isEmpty(username)) {
                    //Join有两种方式                   
//                    Join<Weibo,User> userJoin = root.join("user",JoinType.INNER);
//                    predicates.add(criteriaBuilder.equal(userJoin.get("username"), username));
                    predicates.add(criteriaBuilder.equal(root.get("user").get("username"),username));
                }
                if (!StringUtils.isEmpty(weiboText)) {
                    predicates.add(criteriaBuilder.like(root.get("weiboText"), "%" + weiboText + "%"));
                }
                if(startDate!=null){
                    predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createDate").as(Date.class),startDate));
                }
                if(endDate != null){
                    predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createDate").as(Date.class),endDate));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        },new PageRequest(pageNo,pageSize));
        return page;
    }

可以直接使用findAll方法并创建匿名内部类继承Specification即可,实现toPredicate方法返回最终的条件,但是笔者想在这里使用JoinFetch,通过很多方法也未能在toPredicate实现,希望其他大牛走过路过,留下在这里使用joinFetch的方法。

 

 

五、普通的DAO

当然我们可以使用最为基础的方法去写DAO。直接@Repository 然后@PersistenceContext。最原始最基本~

 

@Repository
public class WeiboDao {

    @PersistenceContext
    private EntityManager entityManager;


    @Transactional(readOnly = true)
    public List<Weibo> searchWeiboByEm(String username, String weiboText, Date startDate, Date endDate, int pageNo, int pageSize) {
        StringBuffer jpql = new StringBuffer("select w from Weibo w join fetch w.user u left join fetch w.comments c where 1=1 ");
        Map<String,Object> paramMap = new HashMap<>();
        if(!StringUtils.isEmpty(username)){
            jpql.append(" and u.username = :username");
            paramMap.put("username",username);
        }
        if(!StringUtils.isEmpty(weiboText)){
            jpql.append(" and w.weiboText like :weiboText");
            paramMap.put("weiboText","%"+weiboText+"%");
        }
        if(startDate!=null){
            jpql.append(" and w.createDate >= :startDate");
            paramMap.put("startDate",startDate);
        }
        if(endDate != null){
            jpql.append(" and w.createDate <= :endDate");
            paramMap.put("endDate",endDate);
        }

        Query query = entityManager.createQuery(jpql.toString());
        Set<String> keys = paramMap.keySet();
        for (String keyItem : keys) {
            query.setParameter(keyItem,paramMap.get(keyItem));
        }
        return query.setFirstResult(pageNo*pageSize).setMaxResults(pageSize).getResultList();
    }

}

 

 

六、声明式事务

SpringBoot默认已经帮我们自动配置好了JPATransaction,我们直接使用即可。

直接在Service层测试@Transactional,当然正常的事务隔离级别还有事务传递方式还是和以前Spring一样配置。注意这个@Transactional是Spring提供的,不要错误的使用JPA的@Transactional

 


@Service
public class WeiboService {

    @Autowired
    private WeiboRepository weiboRepository;

    @Transactional(readOnly = false,isolation = Isolation.READ_COMMITTED)
    public List<Weibo> importWeiboList(List<Weibo> weibos, User user){
        int index = 0;
        Date nowDateTime = new Date(System.currentTimeMillis());
        for (Weibo weiboItem: weibos) {
            weiboItem.setUser(user);
            weiboItem.setCreateDate(nowDateTime);
            if(5<=index++){
                throw new RuntimeException("Weibo out of limit!!!");
            }
            this.weiboRepository.save(weiboItem);
        }
        return weibos;
    }

}

 

posted on 2018-04-17 22:15  yangjingzhi  阅读(358)  评论(0编辑  收藏  举报

导航