JPA 常见用法

3 篇文章 0 订阅

JPA 常见用法

一、主键自增长

1.1 @Id: 指定为主键

1.2 @GeneratedValue:指定主键自增长类型

1.2.1 strategy:主键自增长方式
  • GenerationType.TABLE: 通过一张特定的表来维护

    @Id
    @GeneratedValue(
        strategy = GenerationType.TABLE, 
        generator="pk_gen")
    @TableGenerator(
        name ="pk_gen", // 表示该表主键生成策略的名称
    	table="tb_generator", // 表示表生成策略所持久化的表名
        pkColumnName="gen_name", // 表示在持久化表中,该主键生成策略所对应键值的名称
        valueColumnName="gen_value", // 表示在持久化表中,该主键当前所生成的值,它的值将会随着每次创建累加
        pkColumnValue="PAYABLEMOENY_PK", // 表示在持久化表中,该生成策略所对应的主键
        initialValue=0, // 表示主键初识值,默认为0。
        allocationSize=1 // 每次主键值增加的大小
    )
    
  • GenerationType.SEQUENCE:通过序列来维护

    通常要与注解 @SequenceGenerator 搭配使用

    Mysql 不支持序列、Oracle,PostgreSQL,DB2支持

    @Id
    @GeneratedValue(
        strategy = GenerationType.SEQUENCE,
        generator="workflow_node_seq")
    @SequenceGenerator(
        name="workflow_node_seq", // 表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中。
        sequenceName="workflow_node_id_seq", // 表示生成策略用到的数据库序列名称
        initialValue=0, // 表示主键初识值,默认为0。
        allocationSize=1 // 每次主键值增加的大小
    )
    
  • GenerationType.IDENTITY:通过数据库本身来维护,数据库主键自增长

    大部分数据库均支持,但 sql 设置方式可能略有不同

  • GenerationType.AUTO:持久化引擎来维护自增长

    缺点:多张表使用同一个增长序列

1.2.2generator: 自增长维护规则名称

二、表关联

2.1 @OneToOne: 一对一关系

2.1.1 单向关联
  • User

    @Data
    @Entity
    @Table(name = "t_user")
    public class User { // 主表
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        
        @OneToOne
        @JoinColumn(name = "user_info_id",referencedColumnName = "id") // referencedColumnName 可省略,默认使用主键
        private UserInfo userInfo;
    }
    
    
  • UserInfo

    @Data
    @Entity
    @Table(name = "t_user_info")
    public class UserInfo {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    }
    
2.1.2 双向关联
  • User

    @Data
    @Entity
    @Table(name = "t_user")
    @ToString(exclude = "userInfo") // toString 排除 userInfo 属性,解决 死循环问题
    public class User { // 从表
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    
        @OneToOne
        @JoinColumn(name = "user_info_id",referencedColumnName = "id") // referencedColumnName 可省略,默认使用主键
        private UserInfo userInfo;
    }
    
  • UserInfo

    @Data
    @Entity
    @Table(name = "t_user_info")
    @ToString(exclude = "user")
    public class UserInfo {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    
        @OneToOne
        @JoinColumn(name = "user_id")
        private User user;
    }
    

2.2 @ManyToOne & OneToMany:多对一和一对多关系

2.2.1 单向关联
  • @ManyToOne

    Student

    @Data
    @Entity
    @Table(name = "t_student")
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    
        @ManyToOne
        @JoinColumn(name = "group_id")
        private Group group;
    }
    

    Group

    @Data
    @Entity
    @Table(name = "t_group")
    public class Group {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    }
    
  • @OneToMany

    Group

    @Data
    @Entity
    @Table(name = "t_group")
    public class Group {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    
         // 默认 Lazy 获取不到, 关联属性为 集合时必须 EAGER, 或者在查询方法上加@Transactional(readOnly = true) 注解
        @OneToMany(fetch = FetchType.EAGER)
        @JoinColumn(name = "group_id")
        List<Student> studentList;
    }
    

    Student

    @Data
    @Entity
    @Table(name = "t_student")
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    }
    
2.2.2 双向关联
  • Group

    @Data
    @Entity
    @Table(name = "t_group")
    @ToString(exclude = "studentList") // toString 排除 studentList 属性,解决 死循环问题
    public class Group {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    
         // 默认 Lazy 获取不到, 关联属性为 集合时必须 EAGER,或者在查询方法上加@Transactional(readOnly = true) 注解
        @OneToMany(mappedBy = "group", fetch = FetchType.EAGER)
        List<Student> studentList;
    }
    
  • Student

    @Data
    @Entity
    @Table(name = "t_student")
    @ToString(exclude = "group") // toString 排除 group 属性,解决 死循环问题
    public class Student {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    
        @ManyToOne
        @JoinColumn(name = "group_id")
        private Group group;
    }
    

2.3 @ManyToMany:多对多关系

  • 单向关联

    User

    @Data
    @Entity
    @Table(name = "t_user")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        
         // 默认 Lazy 获取不到, 关联属性为 集合时必须 EAGER,或者在查询方法上加@Transactional(readOnly = true) 注解
        @ManyToMany(fetch = FetchType.EAGER) 
        @JoinTable(name = "t_user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
        private List<Role> roles;
    }
    

    Role

    @Data
    @Entity
    @Table(name = "t_role")
    public class Role {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    }
    
  • 双向关联

    User

    @Data
    @Entity
    @Table(name = "t_user")
    @ToString(exclude = "roles") // toString 排除 roles 属性,解决 死循环问题
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
        
         // 默认 Lazy 获取不到, 关联属性为 集合时必须 EAGER,或者在查询方法上加@Transactional(readOnly = true) 注解
        @ManyToMany(fetch = FetchType.EAGER)
        @JoinTable(name = "t_user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
        private List<Role> roles;
    }
    

    Role

    @Data
    @Entity
    @Table(name = "t_role")
    @ToString(exclude = "userList") // toString 排除 userList 属性,解决 死循环问题
    public class Role {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer id;
    
         // 默认 Lazy 获取不到, 关联属性为 集合时必须 EAGER,或者在查询方法上加@Transactional(readOnly = true) 注解
        @ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER)
        private List<User> userList;
    }
    

2.4 父子表关联

适用于 拆表

2.4.1 单表关联:@SecondaryTable
@Data
@Entity
@Table(name = "t_student")
@SecondaryTable(name = "t_student_info", // 子表表名
        pkJoinColumns = @PrimaryKeyJoinColumn(name = "student_id") // 关联主键
)
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    private String sex;

    private String age;

    @Column(table = "t_student_info")  // 该字段在子表 t_student_info 中
    private String birthDay;
}
2.4.2 多表关联:@SecondaryTables
@Data
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(name = "t_product")
@SecondaryTables({
        @SecondaryTable(name = "t_product_info", pkJoinColumns = @PrimaryKeyJoinColumn(name = "product_id")),
        @SecondaryTable(name = "t_product_detail", pkJoinColumns = @PrimaryKeyJoinColumn(name = "product_id"))
})
public class Product extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(table = "t_product_info")
    private BigDecimal prices;
    @Column(table = "t_product_detail")
    private String brand;
}

三、自动填充

3.1 主类加 @EnableJpaAuditing

@EnableJpaAuditing
@SpringBootApplication
public class JpaMain {
    public static void main(String[] args) {
        SpringApplication.run(JpaMain.class,args);
    }
}

3.2 Config 中 增加 bean

@Configuration
public class AutoFillConfig {
    @Bean
    public AuditorAware<String> auditorAware() {
        AuditorAware<String> aa = () -> {
            // TODO 获取当前用户名
            String username = "测试";
            return Optional.of(username);
        };
        return aa;
    }
}

3.3 自动填充抽象类

@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class  BaseEntity implements Serializable {
    // 创建人
    @CreatedBy
    @Column(length = 50)
    private String createdBy;
    // 创建时间
//    @CreatedDate
    @CreationTimestamp
    private Date createdTime;
    // 更新人
    @LastModifiedBy
    @Column(length = 50)
    private String updatedBy;
    // 更新时间
//    @LastModifiedDate
    @UpdateTimestamp
    private Date updatedTime;
}

四、枚举

枚举:用于避免拼写错误,一定程度上,加密数据,存储原则:

  • 数据库:枚举的真实含义
  • Json:枚举的 代表编号

4.1 注解方式

4.1.1 枚举类
@Getter // 数据库存储时
@AllArgsConstructor
public enum GradeEnum{
    UNKNOWN("未知", 0),
    SECONDARY("中学", 2),
    PRIMARY("小学", 1),
    HIGH("高中", 3);

    private final String name;
	@JsonValue  // 转 JSON 时的字段
    private final Integer value;
}
4.1.2 使用
@Data
@Entity
@Table(name = "t_student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    // 枚举,数据库存储时,存的值为 UNKNOWN、PRIMARY、SECONDARY、HIGH
    @Enumerated(value = EnumType.STRING)
    private GradeEnum grade;
}

4.2 专用转换类

4.2.1 枚举类
@Getter
@AllArgsConstructor
public enum GradeEnum {
    UNKNOWN("未知", 0),
    SECONDARY("中学", 2),
    PRIMARY("小学", 1),
    HIGH("高中", 3);

    private final String name;
    @JsonValue
    private final Integer value;
}
4.2.2 转换类
@Converter(autoApply = true)
public class EnumConverter implements AttributeConverter<GradeEnum, String> {

    /**
     * 此方法告诉jpa数据库里存的值
     */
    @Override
    public String convertToDatabaseColumn(GradeEnum gradeEnum) {
        if (gradeEnum == null) {
            return null;
        }
        return gradeEnum.getName();
    }

    /**
     * 此方法告诉Java从数据库里取出的值对应的是哪个enum值
     */
    @Override
    public GradeEnum convertToEntityAttribute(String value) {
        if (value == null) {
            return null;
        }
        for (GradeEnum gradeEnum : GradeEnum.values()) {
            if (gradeEnum.getName().equals(value)) {
                return gradeEnum;
            }
        }
        return GradeEnum.UNKNOWN;
    }
}
4.2.3 使用
@Data
@Entity
@Table(name = "t_product")
public class Product extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private GradeEnum grade;
}

4.3 通用转换类

4.3.1 枚举类
  • 接口 BaseEnum

    // 枚举通用接口
    public interface BaseEnum<V> {
        // 用于显示的枚举名称
        String getName();
        // 数据库存储数值
        V getCode();
    }
    
  • 枚举类

    @Getter
    @AllArgsConstructor
    public enum GradeEnum implements BaseEnum<Integer>{
        UNKNOWN(0, "未知"),
        SECONDARY(2, "中学"),
        PRIMARY(1, "小学"),
        HIGH(3, "高中");
        private final Integer code;
        @JsonValue
        private final String  name;
    
        // 静态内部类,这里也可不重写方法
        public static class Converter extends BaseEnumConverter<GradeEnum, Integer>{
            @Override // 重写方法,补充 baseEnum 为空 时插入枚举类型 为 UNKNOWN(0, "未知")
            public Integer convertToDatabaseColumn(BaseEnum<Integer> baseEnum) {
                try {
                    return super.convertToDatabaseColumn(baseEnum);
                }catch (Exception e){
                    return 0;
                }
            }
    
            @Override // 重写方法,补充将数据库中读取到的 无法转换的类型,默认转换为 UNKNOWN(0, "未知")
            public BaseEnum<Integer> convertToEntityAttribute(Integer value) {
                try {
                    return super.convertToEntityAttribute(value);
                }catch (Exception e){
                    return GradeEnum.UNKNOWN;
                }
            }
        }
    }
    
4.3.2 转换类
// 抽象类,默认通用转换类
public abstract class BaseEnumConverter<X extends BaseEnum<V>,V> implements AttributeConverter<BaseEnum<V>, V> {

    private Class<X> clazz;
    private Method method;

    @SuppressWarnings("unchecked")
    public BaseEnumConverter() {
        this.clazz = (Class<X>) (((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments())[0];
        try {
            method = clazz.getMethod("values");
        } catch (Exception e) {
            throw new RuntimeException("can't get values method from " + clazz);
        }
    }
    /**
     * 数据库存储的值
     * @param baseEnum 枚举对象
     * @return 数据库中存放的值
     */
    @SuppressWarnings("unchecked")
    @Override
    public V convertToDatabaseColumn(BaseEnum<V> baseEnum) {
        return baseEnum.getCode();
    }

    /**
     * 数据库中的值转换为 枚举对象
     * @param value 数据库中的值
     * @return 枚举对象
     */
    @SuppressWarnings("unchecked")
    @Override
    public BaseEnum<V> convertToEntityAttribute(V value) {
        try {
            X[] enums = (X[]) method.invoke(null);
            for (X x: enums){
                if (x.getCode().equals(value)){
                    return x;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
            throw new IllegalArgumentException("unknown db enum attribute: " + value);
    }
}
4.3.3 使用
@Data
@EqualsAndHashCode(callSuper = false)
@Entity
@Table(name = "t_product")
public class Product extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @Convert(converter = GradeEnum.Converter.class)
    private GradeEnum grade;
}

五、分页

5.1定义统一分页接口

public interface AppPage<T> {

    /**
     * 当前页页码
     */
    long getPageNum();

    /**
     * 每页条数
     */
    long getPageSize();

    /**
     * 总条数
     */
    long getTotal();

    /**
     * 总页数
     */
    long getTotalPages();

    /**
     * 分页对象记录
     */
    List<T> getItems();
}

5.2 接口封装

@AllArgsConstructor
public class JpaPageImpl<T> implements AppPage<T> {
    private Page<T> page;

    @Override
    public long getPageNum() {
        if (page == null) return  0;
        return page.getNumber() + 1;
    }

    @Override
    public long getPageSize() {
        if (page == null ) return 0;
        return page.getSize();
    }

    @Override
    public long getTotal() {
        if (page == null ) return 0;
        return page.getTotalElements();
    }

    @Override
    public long getTotalPages() {
        if (page == null ) return 0;

        return page.getTotalPages();
    }

    @Override
    public List<T> getItems() {
        return page.getContent();
    }
}

5.3 使用

@GetMapping("page")
public  JpaPageImpl<Product> page (){
    PageRequest pageRequest = PageRequest.of(0,2);
    Page<Product> all = productRepo.findAll(pageRequest);
    return new JpaPageImpl<>(all);
}

六、动态查询

6.1 Specification

6.1.1 前提条件:repository 必须实现 JpaSpecificationExecutor 接口
@Repository
public interface StudentRepo extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {}
// 或者,JpaRepositoryImplementation 接口就是将 以上两个接口整合到一起
@Repository
public interface StudentRepo extends JpaRepositoryImplementation<Student, Integer> {}
6.1.2 使用
@SpringBootTest
public class DynamicQueryTest {

    @Autowired
    private StudentRepo studentRepo;
 	
    @Test // 单条件
    public void specification1 (){ 
        // root.get("age") 中 age 为 Student 中 属性名,不是表中列名 
        Specification<Student> spec1 = (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("age"),2);
        List<Student> students1 = studentRepo.findAll(spec1);
        System.out.println(students1);
    }
    
    @Test // 多条件
    public void specification2 (){
        Specification<Student> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(cb.equal(root.get("grade"), 1)); // 不可使用枚举,必须使用数据存储的值
            predicates.add(cb.in(root.get("age")).value(List.of(11,12,14))); // in 查询
            predicates.add(cb.ge(root.get("age"), 2)); //  >= 查询
            predicates.add(cb.le(root.get("age"), 28)); //  <= 查询
            predicates.add(cb.like(root.get("name"), "t%")); //  like 查询
            Predicate[] ps = new Predicate[predicates.size()];
            return cb.and(predicates.toArray(ps));
        };
        List<Student> students = studentRepo.findAll(spec);
        System.out.println(students);
    }
}

6.2 Example

@SpringBootTest
public class DynamicQueryTest {

    @Autowired
    private StudentRepo studentRepo;

    @Test
    public void example (){

        // 查询条件
        Student student = new Student();
        student.setName("ts");
        // 分页条件
        PageRequest pageRequest = PageRequest.of(0,10);
        // 匹配规则
        ExampleMatcher matcher = ExampleMatcher.matchingAll()
                .withStringMatcher(ExampleMatcher.StringMatcher.STARTING) // 所有非空 String 字段进行匹配
//                .withMatcher("name", ExampleMatcher.GenericPropertyMatcher::startsWith) // 指定 字段 匹配
//                .withMatcher("name", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.ENDING)) // 模糊查询,匹配结尾
//                .withMatcher("name", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.CONTAINING)) // 模糊查询,包含
//                .withMatcher("name", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.DEFAULT)) // 默认
//                .withMatcher("name", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.EXACT)) // 精确查询
//                .withMatcher("name", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.REGEX)) // 模糊查询,正则匹配
                .withIgnoreNullValues();
        Example<Student> example = Example.of(student, matcher);
        Page<Student> page = studentRepo.findAll(example, pageRequest);
        System.out.println(page.getTotalPages());
    }
}

七、其他

7.1 @Transient:标记为非持久化字段

标记某字段为业务中的临时存放字段,数据库中不存在

7.2 @Enumerated: 枚举类持久化

  • EnumType.ORDINAL:持久化为 枚举字段下标
  • EnumType.STRING:持久化为 枚举字段 name

7.3 @Lob:持久化为大对象类型

一般要配合 @Basic(fetch=FetchType.LAZY) 延迟加载

  • Clob:长字符串类型
  • Blob:字节类型

7.4 @OrderBy:关联集合元素 的排序

// 下面将根据 sex 递增排序,salary 递减排序
@OneToMany(cascade = CascadeType.ALL)
@OrderBy("sex asc,salary desc")
private List<Student> studentList;

  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
EntityManager是Java Persistence API(JPA)中的一个接口,用于管理实体对象的生命周期,包括创建、更新、删除和查询等操作。下面是EntityManager的常见用法: 1. 获取EntityManager对象 在使用EntityManager之前,需要首先获取EntityManager对象。可以通过EntityManagerFactory来获取EntityManager对象。 ```java EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistence_unit_name"); EntityManager entityManager = entityManagerFactory.createEntityManager(); ``` 2. 创建实体对象 使用EntityManager可以创建新的实体对象。可以通过调用EntityManager的persist()方法来创建一个实体对象,将它保存到数据库中。 ```java Employee employee = new Employee(); employee.setName("John"); employee.setAge(30); entityManager.getTransaction().begin(); entityManager.persist(employee); entityManager.getTransaction().commit(); ``` 3. 更新实体对象 通过更新实体对象,可以将实体对象的属性值更新到数据库中。可以通过EntityManager的merge()方法来更新实体对象。 ```java Employee employee = entityManager.find(Employee.class, 1); employee.setAge(35); entityManager.getTransaction().begin(); entityManager.merge(employee); entityManager.getTransaction().commit(); ``` 4. 删除实体对象 可以通过EntityManager的remove()方法从数据库中删除实体对象。 ```java Employee employee = entityManager.find(Employee.class, 1); entityManager.getTransaction().begin(); entityManager.remove(employee); entityManager.getTransaction().commit(); ``` 5. 查询实体对象 可以使用EntityManager的createQuery()方法来创建查询。查询结果可以使用getResultList()或getSingleResult()方法来获取。 ```java Query query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.age > :age"); query.setParameter("age", 30); List<Employee> employees = query.getResultList(); ``` 6. 执行事务 使用EntityManager可以执行事务。可以通过调用EntityManager的getTransaction()方法来获取Transaction对象。 ```java entityManager.getTransaction().begin(); // 一些操作 entityManager.getTransaction().commit(); ``` 以上是EntityManager的常见用法,还可以使用它来管理实体对象之间的关系等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值