一、简介
Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用Hibernate 的 JPA 技术实现。Hibernate 和Mybatis都是ORM框架,所以可以将SpringDataJPA理解为Mybatis。
二、SpringBoot整合JPA
更多用法参考官网:Spring Data JPA
1.1依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
1.2配置
spring:
jpa:
hibernate:
#更新或者创建数据表结构
ddl-auto: update
#控制台显示SQL
show-sql: true
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=tianya
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
server.port=8010
logging.level.org.springframework=error
#spring.jpa.generate-ddl=
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.type=trace
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.jdbc.batch_size=50
logging.level.org.hibernate.type.descriptor.sql=trace
ddl-auto说明
在jpa中ddl-auto一共有四种:
1、ddl-auto:create ----每次运行该程序,没有表格会新建表格,表内有数据会清空;
2、ddl-auto:create-drop ----每次程序结束的时候会清空表
3、ddl-auto:update ---- 每次运行程序,没有表格会新建表格,表内有数据不会清空,只会更新
4、ddl-auto: validate ---- 运行程序会校验数据与数据库的字段类型是否相同,不同会报错。
一般选择validate/update/none,绝对不能选 create, create-drop,update能帮助建表。
如果希望实体类发生改动而数据库表做出相应的更改且不破坏数据库现有的数据,要将spring.jpa.hibernate.ddl-auto属性值设置为update
这里还有一点,就算把ddl-auto设置成update值,也不能识别对表结构的所有更改,往往只能识别出增加的字段,比如修改字段名,修改字段类型或者删除一个字段都是不能够识别的。
1.3代码
@Entity //告诉JPA这是一个实体类(和数据表映射的类)
@Table(name = "tbl_user") //@Table来指定和哪个数据表对应;如果省略默认表名就是user;
public class User {
@Id //这是一个主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键
private Integer id;
@Column(name = "last_name",length = 50) //这是和数据表对应的一个列
private String lastName;
@Column //省略默认列名就是属性名
private String email;
}
//继承JpaRepository来完成对数据库的操作
public interface UserRepository extends JpaRepository<User,Integer> {
}
1.4无须写SQL
增删改查
Repository相当于Mybatis中的Mapper层,可以在里面写crud接口,所以只需要继承JpaRepository类即可,其它的扩展类还有MongoRepository、CrudRepository、CrudRepository等。举个栗子如下图:
JpaRepository.java
更多介绍参考官网:Spring Data JPA
注解
关键字
1.5需要写SQL
要想查询全部字段可以用 sellect 实体名 这里省略了value ,参数使用了占位置符 ?1 代表第一个参数 ? 2 代表第二个
HQL方式
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
//如果是更新或者删除操作,方法上面要加@Modifying 默认开启的事务只是可读的,更新操作加入@Modifying 就会关闭可读
@Modifying
@Transactional
@Query("update CardConfig set cardStatus=?1 where id in ?2")
void updateCardStatus( Integer status,List<Integer> listIds);
// @Param 代替参数占位符, hql或者sql里就用 :firstname替换 方法里的参数顺序可以打乱
@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(@Param("lastname") String lastname,
@Param("firstname") String firstname);
//返回字段 组成新的entity返回 类名必须是全写的
@Query(value="select new com.hikvision.metro.modules.repository.entity.CameraIndexs(c.preOneCameraIndexcode, c.preTwoCameraIndexcode, c.backOneCameraIndexcode) from StationDeviceConfig c")
List<CameraIndexs> getAllCameraIndexs();
}
SQL方式
@Modifying
@Query(value="select status from t_station_device_config where pre_one_camera_indexcode=?1",nativeQuery = true)
List<Integer> findStatusByPreOneCameraIndexcode(String index);
@Query(value="select radar_indexcode from t_station_device_config",nativeQuery = true)
List<String> getAllRadarIndex();
1.6排序与分页
Service层
@Repository
public interface CardConfigRepository extends JpaRepository<CardConfig,Integer>, JpaSpecificationExecutor {
}
构造Pageable排序
第一种方式:
Sort sort = new Sort(Sort.Direction.DESC, "preDrawIssue").and(new Sort(Sort.Direction.ASC, "regionCode"));
Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
第二种方式:
List<Sort.Order> orders=new ArrayList<>();
orders.add(new Sort.Order(Sort.Direction.DESC,"preDrawIssue"));
orders.add(new Sort.Order(Sort.Direction.ASC,"regionCode"));
Sort sort = Sort.by(orders);
Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
传入Pageable调用
public BaseResult findAll(Pageable pageable, BaseSearch baseSearch) {
List<CardInfo> passInfoBos = new ArrayList<>();
BasePage<CardInfo> basePage = new BasePage<>();
try {
Specification querySpeci = (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = Lists.newArrayList();
if (!StringUtils.isEmpty(baseSearch.getPersonName())) {
predicates.add(criteriaBuilder
.like(root.get("personName"), "%" + baseSearch.getPersonName() + "%"));
}
if (!StringUtils.isEmpty(baseSearch.getCardNo())) {
predicates.add(criteriaBuilder
.like(root.get("cardId"), "%" + baseSearch.getCardNo() + "%"));
}
if (baseSearch.getType() != null) {
predicates.add(criteriaBuilder
.equal(root.get("cardType"), baseSearch.getType()));
}
if (baseSearch.getStatus() != null) {
predicates.add(criteriaBuilder
.equal(root.get("cardStatus"), baseSearch.getStatus()));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
};
//继承了JpaSpecificationExecutor的才会有这个接口
Page<CardConfig> page = cardConfigRepository.findAll(querySpeci, pageable);
if (page != null) {
for (CardConfig info : page.getContent()) {
CardInfo cardInfo = new CardInfo();
cardInfo.convertFromCardConfig(info);
passInfoBos.add(cardInfo);
}
basePage.setList(passInfoBos);
basePage.setTotal(page.getTotalElements());
basePage.setTotalPages(page.getTotalPages());
basePage.setPageSize(page.getSize());
basePage.setPageNo(page.getNumber() + 1);
//LOGGER.info("分页查询卡片列表成功");
} else {
return BaseResult.fail(CommonErrorCode.SYSTEM_INTERNAL_ERROR.getCode(), CommonErrorCode.SYSTEM_INTERNAL_ERROR.getMsg());
}
} catch (BusinessException e) {
throw e;
} catch (AuthorizationException e) {
throw e;
} catch (Exception e) {
LOGGER.errorWithErrorCode(ErrorCode.CARD_CONFIG_QUERY_ERROR.getCode(), ErrorCode.CARD_CONFIG_QUERY_ERROR.getChinaMessage(), e);
throw new MetroBusinessException(ErrorCode.CARD_CONFIG_QUERY_ERROR, e);
}
return new BaseResult(0, "0", "成功", basePage);
}