springboot + JPA

关于ORM框架(Object Relational Mapping 对象关系映射),mybatis 与 JPA各有各的好,都实现了对DAO层(Data Access Object 数据访问对象)功能的强大封装。mybatis可以灵活地手写各种复杂的SQL, 性能也更好;JPA对于扩展实体对象属性字段更友好。

个人建议:追求短平快的小公司可以采用JPA,开发更高效;业务繁杂的大中型企业宜采用mybatis,追求性能与稳定。

JPA(Java Persistence API),(插句题外话:看到一些缩写词,我习惯找出他的全称,有助于理解相关技术的灵魂。)顾名思义就是Java 持久层API,确切地说JPA不是一个技术框架,而是一种接口规范标准,大多习惯用Hibernate框架来实现。

有关springboot的快速搭建配置,请回看历史分享springboot+mybatis。

springboot集成各种功能组件大多采用三步走,一般步骤不外乎:

  1. 依赖 - maven pom文件追加相关依赖
  2. 配置 - yml 文件追加相关配置 或 自定义配置
  3. 封装 - 根据架构设计组装自己的通用组件(工具)

接下来按部就班单刀直入JPA(假定springboot脚手架已搭建好且运行正常)。

1. maven添加JPA依赖

数据源依赖,同mybatis

<!--jpa-->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
    <groupId>org.hibernate.javax.persistence</groupId>
    <artifactId>hibernate-jpa-2.1-api</artifactId>
    <version>1.0.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.2.Final</version>
</dependency>

2. JPA 配置

1.yml(数据源配置,同mybatis)

spring:
  #jpa
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
    properties:
      hibernate:
        format_sql: false
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect # 默认使用innodb存储引擎

2.application(实体类及Repository扫描)

@EntityScan(basePackages = {"com.example.demo.domain"})
@EnableJpaRepositories(basePackages = {"com.example.demo.repository"})
@SpringBootApplication
public class DemoApplication{
  public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3. JPA 功能封装

1) entity

Domain

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Slf4j
@Data
@MappedSuperclass // 该注解作用:该父类不对应映射数据库中的表,但属性可以被子类继承
public class Domain implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Integer id;

    @Column(columnDefinition = "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'")
    protected Date createTime;

    @Column(columnDefinition = "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'")
    protected Date updateTime;

    @JSONField(serialize = false)
    @Column(columnDefinition = "TINYINT(2) NOT NULL DEFAULT 0 COMMENT '删除标志'")
    protected Byte delFlag;

}

User

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

@EqualsAndHashCode(callSuper = true)
@Data
@Entity
@Table(name = "user_user")
@DynamicInsert
@DynamicUpdate
public class User extends Domain {

    @Column(columnDefinition = "VARCHAR(28) NOT NULL DEFAULT '' COMMENT '注册手机号或微信openId'")
    private String account;

    @Column(columnDefinition = "VARCHAR(32) NOT NULL DEFAULT '' COMMENT '注册登录密码'")
    private String password;
}

2) repository

import com.example.demo.domain.Domain;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;

public interface BaseRepository<S extends Domain> extends JpaSpecificationExecutor<S>, JpaRepository<S, Integer> {

    default Specification<S> getSpecification(S example) {
        return (root, criteriaQuery, criteriaBuilder) -> {
            Path<Integer> id = root.get("id");
            Path<Byte> delFlag = root.get("delFlag");

            List<Predicate> predicateList = new ArrayList<>();

            if (example.getId() != null && example.getId() > 0) {
                predicateList.add(criteriaBuilder.equal(id, example.getId()));
            }
            if (example.getDelFlag() != null) {
                predicateList.add(criteriaBuilder.equal(delFlag, example.getDelFlag()));
            }

            return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
        };
    }

}
import com.example.demo.domain.User;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;

public interface UserRepository extends BaseRepository<User> {

    default Specification<User> getSpecification(User example) {
        return (root, criteriaQuery, criteriaBuilder) -> {
            Path<Integer> id = root.get("id");
            Path<String> account = root.get("account");
            Path<Byte> delFlag = root.get("delFlag");

            List<Predicate> predicateList = new ArrayList<>();

            if (example.getId() != null && example.getId() > 0) {
                predicateList.add(criteriaBuilder.equal(id, example.getId()));
            }
            if (example.getDelFlag() != null) {
                predicateList.add(criteriaBuilder.equal(delFlag, example.getDelFlag()));
            }
            if (!StringUtils.isEmpty(example.getAccount())) {
                predicateList.add(criteriaBuilder.equal(account, example.getAccount()));
            }

            return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
        };
    }

    User findByAccount(String account);

    // native SQL
    @Query(value = "select u.* from user_user u where u.id = ?1 or u.account = ?2 ", nativeQuery = true)
    User queryUser(Integer id, String account);

    // JPQL
    @Modifying // 更新或删除时需要加此注解,查询时不需要
    @Query("update User set account = :account where id = :id ")
    User updateUserAccount(@Param("id") Integer id, @Param("account") String account);
}

3) service

BaseService:

import com.example.demo.po.PagerRequest;
import org.springframework.data.domain.Page;

import java.util.List;

public interface BaseService<T> {

    /**
     * saveOrUpdate
     * @param domain
     * @return
     */
    T merge(T domain);

    /**
     * saveOrUpdate All
     * @param domains
     * @return
     */
    List<T> mergeAll(Iterable<T> domains);

    /**
     * 删除
     * @param domain
     */
    void delete(T domain);

    /**
     * 批量删除
     * @param domains
     */
    void deleteAll(Iterable<T> domains);

    /**
     * 按主键查询
     * @param id
     * @return
     */
    T findById(Integer id);

    /**
     * 按主键批量查询
     * @param ids
     * @return
     */
    List<T> findAllById(Iterable<Integer> ids);

    /**
     * 按条件查询数据列表
     * @param domain
     * @return
     */
    List<T> findByExample(T domain);

    /**
     * 按条件统计数量
     * @param domain
     * @return
     */
    long count(T domain);

    /**
     * 分页查询数据列表
     * @param pagerRequest
     * @return
     */
    Page<T> findByPage(PagerRequest pagerRequest);

}
import com.example.demo.domain.Domain;
import com.example.demo.po.PagerRequest;
import com.example.demo.repository.BaseRepository;
import com.example.demo.service.BaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.ParameterizedType;
import java.util.Collections;
import java.util.List;

@Slf4j
public abstract class BaseServiceImpl<T extends Domain> implements BaseService<T> {

    @Autowired
    private BaseRepository<T> baseRepository;

    private Class<T> domain;

    public BaseServiceImpl() {
        ParameterizedType parameterizedType = ((ParameterizedType) getClass().getGenericSuperclass());
        domain = (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public T merge(T domain) {
        return baseRepository.save(domain);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<T> mergeAll(Iterable<T> domains) {
        return baseRepository.saveAll(domains);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(T domain) {
        baseRepository.delete(domain);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteAll(Iterable<T> domains) {
        baseRepository.deleteAll(domains);
    }

    @Override
    public T findById(Integer id) {
        if(id != null && id > 0){
            return baseRepository.findById(id).orElse(null);
        }
        return null;
    }

    @Override
    public List<T> findAllById(Iterable<Integer> ids) {
        if(ids != null){
            return baseRepository.findAllById(ids);
        }
        return Collections.emptyList();
    }

    @Override
    public List<T> findByExample(T domain) {
        return baseRepository.findAll(baseRepository.getSpecification(domain));
    }

    @Override
    public long count(T domain) {
        return baseRepository.count(baseRepository.getSpecification(domain));
    }

    @Override
    public Page<T> findByPage(PagerRequest pagerRequest) {
        try {
            T t = domain.newInstance();

            // 查询参数封装转换 (如分页查询User, 创建UserRequest extends PagerRequest,
            BeanUtils.copyProperties(pagerRequest, t);

            // 排序处理
            Sort sort = pagerRequest.getSort();
            if (sort == null) {
                sort = new Sort(Sort.Direction.DESC, "id");
            }

            // JPA分页从0开始, mybatis pagerHelper从1开始
            PageRequest pageable = PageRequest.of((pagerRequest.getPageNum() - 1), pagerRequest.getPageSize(), sort);
            // 在对应的repository中覆写getSpecification方法
            return baseRepository.findAll(baseRepository.getSpecification(t), pageable);
        } catch (Exception e) {
            log.error("异常信息:{}", e.getMessage());
            throw new RuntimeException("查询参数异常");
        }
    }

}

UserService:

import com.example.demo.domain.User;

public interface UserService extends BaseService<User> {

    /**
     * findBy + 实体对象属性,JPA会自动映射转换成查询SQL
     * @param account
     * @return
     */
    User findByAccount(String account);

    User queryUser(Integer id, String account);

    User updateUserAccount(Integer id, String account);
}
import com.example.demo.domain.User;
import com.example.demo.repository.UserRepository;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends BaseServiceImpl<User> implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public  User findByAccount(String account) {
        return userRepository.findByAccount(account);
    }
  
    @Override
    public User queryUser(Integer id, String account) {
        return userRepository.queryUser(id, account);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User updateUserAccount(Integer id, String account) {
        return userRepository.updateUserAccount(id, account);
    }

}

写在最后:ORM一般作为关系型数据库的持久层处理解决方案,对于非关系型数据库(也就是我们常说的NoSQL), 比如:redis, mongoDB, ES, solr等(虽然有些侧重搜索,但也都具有数据存储功能),spring也可以无缝集成,下次聊聊springboot + springdata,其实JPA也是基于springdata。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值