关于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集成各种功能组件大多采用三步走,一般步骤不外乎:
- 依赖 - maven pom文件追加相关依赖
- 配置 - yml 文件追加相关配置 或 自定义配置
- 封装 - 根据架构设计组装自己的通用组件(工具)
接下来按部就班单刀直入JPA(假定springboot脚手架已搭建好且运行正常)。
1. maven添加JPA依赖数据源依赖,同mybatis
2. JPA 配置org.springframework.data spring-data-jpaorg.hibernate hibernate-coreorg.hibernate.javax.persistence hibernate-jpa-2.1-api1.0.0.Final org.hibernate hibernate-validator5.4.2.Final
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 baseRepositoryextends JpaSpecificationExecutor, JpaRepository{ default SpecificationgetSpecification(S example) { return (root, criteriaQuery, criteriaBuilder) -> { Pathid = root.get("id"); Path delFlag = root.get("delFlag"); List 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{ default Specification getSpecification(User example) { return (root, criteriaQuery, criteriaBuilder) -> { Path id = root.get("id"); Path account = root.get("account"); Path delFlag = root.get("delFlag"); List 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 merge(T domain); List mergeAll(Iterable domains); void delete(T domain); void deleteAll(Iterable domains); T findById(Integer id); List findAllById(Iterable ids); List findByExample(T domain); long count(T domain); Page 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 baseServiceImplimplements baseService { @Autowired private baseRepository baseRepository; private Class domain; public baseServiceImpl() { ParameterizedType parameterizedType = ((ParameterizedType) getClass().getGenericSuperclass()); domain = (Class ) parameterizedType.getActualTypeArguments()[0]; } @Transactional(rollbackFor = Exception.class) @Override public T merge(T domain) { return baseRepository.save(domain); } @Transactional(rollbackFor = Exception.class) @Override public List mergeAll(Iterable 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 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 findAllById(Iterable ids) { if(ids != null){ return baseRepository.findAllById(ids); } return Collections.emptyList(); } @Override public List findByExample(T domain) { return baseRepository.findAll(baseRepository.getSpecification(domain)); } @Override public long count(T domain) { return baseRepository.count(baseRepository.getSpecification(domain)); } @Override public Page 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 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 baseServiceImplimplements 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。



