JPA

JPA(Java Persistence API)。主要是为了简化现有的持久化开发工作和整合ORM技术。JPA是一套规范,不是一套产品。

1. 引用

教程Ref:https://spring.io/guides/gs/accessing-data-jpa/

官方文檔Ref:http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/

中文版指南:https://ityouknow.gitbooks.io/spring-data-jpa-reference-documentation/content/

2. 依賴

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

配置:

# 配置数据库
spring.jpa.database = sql_server
# 查询时是否显示日志
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = none
# Naming strategy
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2008Dialect

3. Annotations

3.1 Entity的註解

  • @Entity。對應關係型DB表
  • @Document。支持Mongo表
  • @Id。對應主鍵
  • @GeneratedValue(strategy=GenerationType.AUTO)。主键的产生策略。
    • Identity:表自动增长字段,Oracle不支持这种方式;
    • AUTO:JPA自动选择合适的策略,是默认选项;
    • Sequence:通过序列产生主键,通过@SequenceGenerator注解指定序列名,Mysql不支持这种方式。
    • TABLE:通过表产生主键,框架借由表模拟产生主键,使用该策略可以使用更易于数据库的移植。
  • @Lob。NVARCHAR(max)
  • @Column。對應列名。
  • @Temporal。时间类型。
    • TemporalType.DATE
    • TemporalType.TIME
    • TemporalType.TIMESTAMP

3.2 Repository的註解

  • @Query(value = "select * from t_userinfo limit ?1", nativeQuery =true)
  • @Transactional

4. Repository

Spring Data JPA creates an implementation on the fly when you run the application.

  • Repository
  • CrudRepository
  • PagingAndSortingRepository

4.1 CrudRepository

package hello;
import java.util.List;
import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Long> {
    List<Customer> findByLastName(String lastName);
}

默認方法:

userRepository.findAll();
userRepository.findOne(1l);
userRepository.save(user);
userRepository.delete(user);
userRepository.count();
userRepository.exists(1l);

自定義簡單查詢:

findXXBy,readAXXBy,queryXXBy,countXXBy, getXXBy。

複雜查詢:

Page<User> findByUserName(String userName,Pageable pageable);

@Test
public void testPageQuery() throws Exception {
    int page=1,size=10;
    Sort sort = new Sort(Direction.DESC, "id");
    Pageable pageable = new PageRequest(page, size, sort);
    userRepository.findALL(pageable);
    userRepository.findByUserName("testName", pageable);
}

@Transactional
@Modifying
@Query("delete from User where id = ?1")
void deleteByUserId(Long id);

4.2 PagingAndSortingRepository

PagingAndSortingRepository 接口继承于 CrudRepository 接口,拥有CrudRepository 接口的所有方法, 并新增两个方法:分页和排序。 但是这两个方法不能包含筛选条件

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {

    /**
     * Returns all entities sorted by the given options.
     * 
     * @param sort
     * @return all entities sorted by the given options
     */
    Iterable<T> findAll(Sort sort);

    /**
     * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
     * 
     * @param pageable
     * @return a page of entities
     */
    Page<T> findAll(Pageable pageable);
}

Sample:

@Test  
    public void test_findAll_page(){  
        int currentPage =0; //当前页从0 开始  
        int pageSize = 5;  

        //排序  
        Order idOrder = new Order(Direction.DESC, "id");  
        Order nameOrder = new Order(Direction.ASC,"name");  
        Sort sort = new Sort(idOrder,nameOrder);  
        PageRequest pageRequest  = new PageRequest(currentPage, pageSize, sort);  

        Page<StudentPO> page = this.studentdPageSortRepository.findAll(pageRequest);  
        System.out.println("总记录数:" + page.getTotalElements());  
        System.out.println("总页数:" + page.getTotalPages());  
        System.out.println("当前页(request):" + page.getNumber());  
        System.out.println("当前页总记录数(request):" + page.getSize());  
        System.out.println("当前页记录总数:" + page.getNumberOfElements());  
        List<StudentPO> students = page.getContent();  
        for (StudentPO studentPO : students) {  
            System.out.println(studentPO);  
        }  
    }
  • 排序語法構造:Sort sort = new Sort(Direction.DESC, "sort").and(new Sort(Direction.DESC, "id"));

5. Test

    @Bean
    public CommandLineRunner demo(CustomerRepository repository) {
        return (args) -> {
            // save a couple of customers
            repository.save(new Customer("Jack", "Bauer"));
            repository.save(new Customer("Chloe", "O'Brian"));
            repository.save(new Customer("Kim", "Bauer"));
            repository.save(new Customer("David", "Palmer"));
            repository.save(new Customer("Michelle", "Dessler"));

            // fetch all customers
            log.info("Customers found with findAll():");
            log.info("-------------------------------");
            for (Customer customer : repository.findAll()) {
                log.info(customer.toString());
            }
            log.info("");

            // fetch an individual customer by ID
            Customer customer = repository.findOne(1L);
            log.info("Customer found with findOne(1L):");
            log.info("--------------------------------");
            log.info(customer.toString());
            log.info("");

            // fetch customers by last name
            log.info("Customer found with findByLastName('Bauer'):");
            log.info("--------------------------------------------");
            for (Customer bauer : repository.findByLastName("Bauer")) {
                log.info(bauer.toString());
            }
            log.info("");
        };
    }

6. 坑

6.1 MS SQL Exception: Incorrect syntax near '@P0'

解決:MS SQL TOP需要加上括號

SELECT TOP (?)

使用2008的方言:

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.SQLServer2008Dialect

Ref:Upgraded hibernate to version 5.x and came across this issue. Had to update "hibernate.dialect" configuration from org.hibernate.dialect.SQLServerDialect to org.hibernate.dialect.SQLServer2012Dialect.

6.2 映射NVARCHAR

NVARCHAR(MAX)使用@Lob來完成映射

6.3 映射UNIQUEIDENTIFIER

用類型UUID不行,改為String即可。

@Id
@GenericGenerator(name = "generator", strategy = "guid", parameters = {})
@GeneratedValue(generator = "generator")
@Column(name = "APPLICATION_ID" , columnDefinition="uniqueidentifier")
private String id = UUID.randomUUID().toString();

6.4 hb5.0命名策略

Ref:https://github.com/hibernate/hibernate-orm/blob/5.0/migration-guide.adoc#naming-strategies

# 有 ImprovedNamingStrategy的效果。
org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

# 有 DefaultNamingStrategy的效果。
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

6.5 多條件查詢與分頁

spring Data JPA支持JPA2.0的Criteria查询,相应的接口是JpaSpecificationExecutor。

Criteria 查询:是一种类型安全和更面向对象的查询 。

private class MySpec implements Specification<ResourceItem> {

    private GetResourceItemListRequestDTO requestDTO;

    public MySpec(GetResourceItemListRequestDTO requestDTO) {
        this.requestDTO = requestDTO;
    }

    @Override
    public Predicate toPredicate(Root<ResourceItem> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

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

        list.add(cb.equal(root.get("applicationID"), requestDTO.getApplicationId()));
        list.add(cb.equal(root.get("categoryID"), requestDTO.getCategoryId()));

        Predicate[] p = new Predicate[list.size()];

        return cb.and(list.toArray(p));
    }
}

var query = repository.findAll(new MySpec(requestDTO),new PageRequest(requestDTO.getPage(), requestDTO.getSize()));

手殘的:PageRequest的page與size傳遞反了。

6.6 映射tinyint

对应java的Byte类型。

7. JPA中使用Convertor

AttributeConverter

  • 持久化enum
  • 加解密数据
  • 持久化日期

轉換枚舉值:

@Convert(converter = TransactionOperation.Converter.class)
private TransactionOperation operationType;

public enum TransactionOperation {
    PAY(1, "收款"),
    AUTHORIZE(3, "預授權"),

        @Getter
    private int code;

    @Getter
    private String chineseName;

    TransactionOperation(int code, String name_ch) {
        this.code = code;
        this.chineseName = name_ch;
    }

    @javax.persistence.Converter(autoApply = true)
    public static class Converter implements AttributeConverter<TransactionOperation, String> {
        @Override
        public String convertToDatabaseColumn(TransactionOperation transactionOperation) {
            return transactionOperation.name();
        }

        @Override
        public TransactionOperation convertToEntityAttribute(String s) {
            return TransactionOperation.valueOf(s);
        }
    }
}

JPA中的日期轉換:

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.sql.Timestamp;
import java.time.LocalDateTime;

@Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
        return (locDateTime == null ? null : Timestamp.valueOf(locDateTime));
    }

    @Override
    public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
        return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
    }
}

results matching ""

    No results matching ""