JPA基本使用

SpringBoot整合JPA

JPA使用步骤

  1. 导入依赖
  2. 编写jpa配置
  3. 编写实体类,并添加JPA相关注解,用于实现实体类与数据表的关联
  4. 编写dao层,创建数据访问接口

1、导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--lombok 注解插件-->
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.16.10</version>
</dependency>

2、修改application.properties文件

# 配置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/sjk?   
      serverTimezone= Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=java
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

3、JPA实体类注解

@Entity
@Entity标记在类名上面,作为实体类的标识

@Table

  1. 当实体类与其映射的数据库表名不同名时需要使用 @Table 标注说明,该标注与 @Entity 标注并列使用,置于实体类声明语句之前,可写于单独语句行,也可与声明语句同行。
  2. @Table 标注的常用选项是 name,用于指明数据库的表名
  3. @Table标注还有一个两个选项 catalog 和 schema 用于设置表所属的数据库目录或模式,通常为数据库名。uniqueConstraints选项用于设置约束条件,通常不须设置。

@Id

  1. @Id设置对象表示符,标识的实体类的属性映射对应表中的主键

@GeneratedValue
设置标识符的生成策略,常与@Id一起使用
参数:strategy指定具体的生成策略

  • 方式一:@GeneratedValue(strategy=GenerationType.AUTO) 也是默认策略, 即写成@GeneratedValue也可;
    类似于hibernate的native策略,生成方式取决于底层的数据库。
  • 方式二:@GeneratedValue(strategy = GenerationType.IDENTITY)指定“自动增长”策略,适用于MySQL;
  • 方式三:@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = “seq_tbl_person”)指定“序列”策略,常用于Oracle,其中generator表示生成器的名字。而且还要指定@SequenceGenerator(name = “seq_tbl_person”, sequenceName = “seq_tbl_person”, allocationSize = 1)注解配合使用
    其中name指定生成器的名字(与generator的值一样),sequenceName指定数据库中定义序列的名字,allocationSize指定序列每次增长1

@Column
描述数据库表中该字段的定义,具有一下属性

  1. name:表示数据库表中该字段的名称,默认情形属性名称一致。
  2. nullable:表示该字段是否允许为null,默认为true。
  3. unique:表示该字段是否是唯一标识,默认为false。
  4. length:表示该字段的大小,仅对String类型的字段有效。
  5. insertable:表示在ORM框架执行插入操作时,该字段是否应出现INSETRT语句中,默认为true。
  6. updateable:表示在ORM框架执行更新操作时,该字段是否应该出现在UPDATE语句中,默认为true。对于一经创建就不可以更改的字段,该属性非常有用,如对于birthday字段。
  7. columnDefinition:表示该字段在数据库中的实际类型。通常ORM框架可以根据属性类型自动判断数据库中字段的类型,但是对于Date类型仍无法确定数据库中字段类型究竟是DATE,TIME还是TIMESTAMP。此外,String的默认映射类型为VARCHAR,如果要将String类型映射到特定数据库的BLOB或TEXT字段类型,该属性非常有用。

@OrderBy
在加载数据的时候可以为其指定顺序。

@Transient
表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性。如果一个属性并非数据库表的字段映射。就务必将其标示为@Transient。否则。ORM框架默认其注解为@Basic

@OneToOne
描述一个一对一的关联
可选

  1. fetch:表示抓取策略,默认为FetchType.LAZY
  2. cascade:表示级联操作策略

@ManyToOne
表示一个多对一的映射,该注解标注的属性通常是数据库表的外键

  1. optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true
    可选
  2. fetch:表示抓取策略,默认为FetchType.EAGER
  3. cascade:表示默认的级联操作策略,可以指定为ALL,PERSIST,MERGE,REFRESH和REMOVE中的若干组合,默认为无级联操作
  4. targetEntity:表示该属性关联的实体类型。该属性通常不必指定,ORM框架根据属性类型自动判断targetEntity。

@OneToMany
描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段。

  1. fetch:表示抓取策略,默认为FetchType.LAZY,因为关联的多个对象通常不必从数据库预先读取到内存
    可选
  2. cascade:表示级联操作策略,对于OneToMany类型的关联非常重要,通常该实体更新或删除时,其关联的实体也应当被更新或删除
    例如:实体User和Order是OneToMany的关系,则实体User被删除时,其关联的实体Order也应该被全部删除

@ManyToMany
描述一个多对多的关联.多对多关联上是两个一对多关联,但是在ManyToMany描述中,中间表是由ORM框架自动处理
可选

  1. targetEntity:表示多对多关联的另一个实体类的全名,例如:package.Book.class
  2. mappedBy:表示多对多关联的另一个实体类的对应集合属性名称
    两个实体间相互关联的属性必须标记为@ManyToMany,并相互指定targetEntity属性,
    需要注意的是,有且只有一个实体的@ManyToMany注解需要指定mappedBy属性,指向targetEntity的集合属性名称
    利用ORM工具自动生成的表除了User和Book表外,还自动生成了一个User_Book表,用于实现多对多关联

@JoinColumn
可选
@JoinColumn和@Column类似,介量描述的不是一个简单字段,而一一个关联字段,例如.描述一个@ManyToOne的字段.

  1. name:该字段的名称.由于@JoinColumn描述的是一个关联字段,如ManyToOne,则默认的名称由其关联的实体决定.例如,实体Order有一个user属性来关联实体User,则Order的user属性为一个外键,
    其默认的名称为实体User的名称+下划线+实体User的主键名称

@JoinTable(name = “student_teacher”, inverseJoinColumns = @JoinColumn(name = “tid”), joinColumns = @JoinColumn(name = “sid”))
可选:由第三张表来维护两张表的关系
name:是关系表的名字
joinColumns:自己这一端的主键
inverseJoinColumns:对方的主键

@MappedSuperclass
可选@MappedSuperclass可以将超类的JPA注解传递给子类,使子类能够继承超类的JPA注解

@Embedded
@Embedded将几个字段组合成一个类,并作为整个Entity的一个属性.
例如User包括id,name,city,street,zip属性.
我们希望city,street,zip属性映射为Address对象.这样,User对象将具有id,name和address这三个属性.
Address对象必须定义为@Embededable

3.1、实体类注解实例

@Entity
@Table(name = "aoa_user")
public class User {

    @Id
    @Column(name = "user_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;        //用户id

    @Column(name = "user_name")
    @NotEmpty(message = "用户名不能为空")
    private String userName;    //登录用户名

    @Column(name = "user_tel")
    @NotEmpty(message = "电话不能为空")
    private String userTel;        //用户电话

    @Column(name = "real_name")
    @NotEmpty(message = "真实姓名不能为空")
    private String realName;    //真实姓名

    private String pinyin;

    @NotEmpty(message = "邮箱不能为空")
//    @Pattern(regexp = "^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\\.[a-zA-Z0-9_-]{2,3}){1,2})$", message =
//			"请填写正确邮箱号")
    private String eamil;        //邮件

    @NotEmpty(message = "地址不能为空")
    private String address;        //地址

    @Column(name = "user_edu")
    @NotEmpty(message = "学历不能为空")
    private String userEdu;        //用户学历

    private Boolean superman = false;

    @Column(name = "user_school")
    @NotEmpty(message = "毕业院校不能为空")
    private String school;        //学校

    @Column(name = "user_idcard")
//    @Pattern(regexp = "^(\\d{6})(19|20)(\\d{2})(1[0-2]|0[1-9])(0[1-9]|[1-2][0-9]|3[0-1])(\\d{3})(\\d|X|x)" +
//			"?$", message = "请填写正确身份证号")
    private String idCard;        //用户身份证

    @NotEmpty(message = "卡号不能为空")
//    @Length(min = 16, max = 19, message = "银行卡号长度必须在16到19之间!")
    private String bank;        //银行

    private String sex;            //性别

    @Column(name = "theme_skin")
    private String themeSkin;    //主题皮肤

    private Date birth;            //生日

    @Column(name = "user_sign")
    private String userSign;    //用户签名

    private String password;    //用户密码

    private String salary;        //用户薪水

    @Column(name = "img_path")
    private String imgPath;        //用户头像路径

    @Column(name = "hire_time")
    private Date hireTime;        //入职时间

    @Column(name = "is_lock")
    private Integer isLock = 0;        //该用户是否被禁用

    @Column(name = "last_login_ip")
    private String lastLoginIp;    //用户最后登录ip;

    @Column(name = "last_login_time")
    private Date lastLoginTime;    //最后登录时间

    @Column(name = "modify_time")
    private Date modifyTime;        //最后修改时间

    @Column(name = "modify_user_id")
    private Long modifyUserId;    //最后修改此用户的用户id

    @Column(name = "father_id")
    private Long fatherId;        //上司id

    private Integer holiday;   //请假天数
    
    @ManyToOne()
    @JoinColumn(name = "position_id")
    private Position position;    //外键关联 职位表

    @ManyToOne()
    @JoinColumn(name = "dept_id")
    private Dept dept;            //外键关联 部门表

    @ManyToOne()
    @JoinColumn(name = "role_id")
    private Role role;            //外键关联 角色表

    @ManyToMany(mappedBy = "users")
    private List<ScheduleList> scheduleLists;

    @ManyToMany(mappedBy = "users")
    private List<Reply> replys;

    @ManyToMany(mappedBy = "users")
    private List<Discuss> discuss;

    @ManyToMany(mappedBy = "userss")
    private List<Note> note;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Attends> aSet;
	
	// setter getter 方法略
	.......
}

4、创建数据访问接口

继承JpaRepository类,或者是PagingAndSortingRepository类,在这两个类对象中有着一些公共的操作数据库的方法,继承后,可以直接进行使用。

public interface orderMasterRepository extends JpaRepository<表名, 表的主键字段类型> {}
public interface PositionDao extends PagingAndSortingRepository<Position, Long>{}
//   PagingAndSortingRepository继承自CrudRepository接口,所以除了拥有CrudRepository的功能之外,还增加了排序和分页查询的功能。

4.1、JpaRepository

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    List<T> findAll();

    List<T> findAll(Sort var1);

    List<T> findAllById(Iterable<ID> var1);

    <S extends T> List<S> saveAll(Iterable<S> var1);

    void flush();

    <S extends T> S saveAndFlush(S var1);

    void deleteInBatch(Iterable<T> var1);

    void deleteAllInBatch();

    T getOne(ID var1);

    <S extends T> List<S> findAll(Example<S> var1);

    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

4.2、PagingAndSortingRepository

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort var1);

    Page<T> findAll(Pageable var1);
}

4.3、CrudRepository

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> S save(S var1);

    <S extends T> Iterable<S> saveAll(Iterable<S> var1);

    Optional<T> findById(ID var1);

    boolean existsById(ID var1);

    Iterable<T> findAll();

    Iterable<T> findAllById(Iterable<ID> var1);

    long count();

    void deleteById(ID var1);

    void delete(T var1);

    void deleteAll(Iterable<? extends T> var1);

    void deleteAll();
}

4.3、JPA常用的公共方法

1)	list<?> findAll()		// 查询所有数据,返回list对象
List<OrderDetail> orderDetailList = orderDetailRepository.findAll();2save():			  	   // 添加数据,返回插入成功的对象
orderMasterRepository.save(orderMaster);3saveAndFlush(T)			  // 保存与更新,返回更新或者插入成功的对象
productCategoryRepository.saveAndFlush(productCategory);4findById(主键id)   		// 返回Optional对象
productCategoryRepository.findById(1).get();
Optional对象方法,查询成功使用get返回定义实体类对象,否则返回null

(5deleteById(主键ID)	   // 根据主键id删除对象信息,没有返回值
orderDetailRepository.deleteById("1");6count()				  // 统计对象的记录条数(不怎么常用),返回long型数据
Long l = orderDetailRepository.count();

5、分页查询

jpa已经帮我们实现了分页的功能(直接使用即可),在查询的方法中,需要传入参数Pageable
,当查询中有多个参数的时候Pageable建议做为最后一个参数传入。

Page<User> findAll(Pageable pageable);
	// 传入Pageable对象后,jpa会自动根据此对象进行数据的分页显示

//    按照单个条件排序,并分页查询
@GetMapping("/getPage")
public Page<Stucls> PageQuery() throws Exception {
    Sort sort = Sort.by(Sort.Direction.ASC, "clsBh");
    // 构建Pageable对象 
    Pageable pageable = PageRequest.of(1,2,sort);
    // 根据传入的Pageable对象进行分页
    return stuCls.findAll(pageable);
}

// 按照多个指定条件排序,并且进行分页查询
@GetMapping("/getPageAll")
public Page<Stucls> PageQuery() throws Exception {
	List<Order> orders = new ArrayList<>();
	// 添加多个排序条件
	orders.add(new Order(Direction.DESC, "clsBh"));
	orders.add(new Order(Direction.DESC, "clsName"));
	....
	// 将集合放入Sort排序对象中
    Sort sort = Sort.by(orders);
    // 构建Pageable对象 
    Pageable pageable = PageRequest.of(1,2,sort);
     // 根据传入的Pageable对象进行分页
    return stuCls.findAll(pageable);
}

Page对象常用方法

// Page是一个泛型接口,代表查询的单页结果集,也有其他信息。常用以下方法
int getTotalPages() //返回总的页数
long getTotalElements() //返回总行数
List<T> getContent() //返回查询结果集的List

// Pageable接口,常用于构造翻页查询,通常也有以下方法
int getPageNumber() //获取总页数
int getPageSize() //获取一页的行数
Pageable next() //返回Pageable类型的下一页
boolean hasPrevious() //是否有上一页

//Sort是一个用于排序的类型,默认升序排序,通常有以下用法
Sort sort = Sort.by(Direction.DESC,"id");

6、JPA自定义方法

6.1、方法命名查询

自定义的简单查询就是根据方法名来自动生成SQL,主要的语法是findByxxx,readByxxx,queryByxxx,getByxxx后面跟属性名称(实体对对象里面的属性,开头字母大写):同时支持And和or等连接词当做条件的拼接

查询的数据的方式比较多,可以根据具体语义选择不同的查询。下面演示根据订单id查询数据的所有方法。

//(1) 普通查询    =>  findBy +属性名
User findById(Long id);
User readById(Long id);
User queryById(Long id);
User getById(Long id);

// findBy + 属性名称(根据属性名称进行完成匹配的查询)

//(2)条件查询  =>     findBy +属性名称+“查询方式(Like| isnull)
User findByUserNameLike(String custName)    
		// 表示根据CustName字段进行模糊查询

//(3)多条件查询  =>   findBy +属性名+“查询方式”+ “多条件的连接符(and|or) ”+属性名+“查询方式”
findByIdLikeOrProductId("123456%","956434");
// 查询User对象Id字段进行模糊匹配,或者查询ProductId字段精准匹配

//(4)集合查询
List<User> findByIdIn(List<Long> id);
	// 表示使用In关键字进行查询,如果后面不加集合查询关键字则会报Operator SIMPLE_PROPERTY on users requires a scalar argument异常

6.2、自定义SQL查询

@Modifying作用:

(1)可以通过自定义的 JPQL 完成 UPDATE 和 DELETE 操作。 注意: JPQL 不支持使用 INSERT
(2)在 @Query 注解中编写 JPQL 语句, 但必须使用 @Modifying 进行修饰. 以通知 SpringData, 这是一个 UPDATE 或 DELETE 操作
(3)UPDATE 或 DELETE 操作需要使用事务,此时需要定义 Service 层,在 Service 层的方法上添加事务操作;
(4)默认情况下, SpringData 的每个方法上有事务, 但都是一个只读事务。 他们不能完成修改操作。
(5)jpa注解操作必须设置别名,否则jpa会报错(设置别名,意思是把实体类中设置的表名拿出来进行数据库操作)

// (1)根据对象操作
 @Transactional
 @Modifying
 @Query("update OrderDetail as u set u.productName = ?1 where u.orderId = ?2")
public void updateByOrderId(String userName,String OrderId);
			//注意:?1表示,引用方法参数的第一个参数,?2表示,引用方法参数的第二个参数
//(2)根据对象操作
@Transactional
@Modifying
@Query("delete from OrderDetail where orderName like %:OrderId%")   
void deleteByOrderId(String OrderName);

//(3)	nativeQuery = true根据原SQL操作 
@Transactional(rollbackOn = Exception.class)
@Modifying
@Query(value = "delete from aoa_role_power_list where role_id=:rId",nativeQuery = true)
Integer deleteByRoleId(Long rId);

6.3、动态组合查询

前提

DAO必须继承JpaSpecificationExecutor<实体类名>

JpaSpecificationExecutor类常用方法

T findById (Specification<T> spec); //查询单个对象
List<T> findAll (Specification<T> spec) ; // 查询列表

//查询全部,分页
//pageable:分页参数
//返回值:分页pageBean (page: 是springdatajpa提供的 )
Page<T> findAll (Speci fication<T> spec, Pageable pageable) ;

//查询列表
//Sort:排序参数
List<T> findAll(Speci fication<T> spec, Sort sort) ;
long count(Specification<T> spec) ;//统计查询

Specification查询
自定义我们自己的specification实现类

// 实现
//root:查询的根对象( 查询的任何属性都可以从根对象中获取)
//CriteriaQuery:顶层查询对象,自定义查询方式(了解: 一般不用)
//CriteriaBuilder:查询的构造器,封装了很多的查询条件
Predicate toPredicate (Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); 
					//封装查询条件
单查询条件

模糊查询

 List<Student> all = stuDAO.findAll(new Specification<Student>() {
            @Override
            public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//              (1) 获取需要查询的属性
                Path<Object> stuUser = root.get("stuUser");
                Predicate like = cb.like(stuUser.as(String.class), "王%");
//              将结果返回
                return like;
            }
        });
        System.out.println(all);

排序查询

Sort sort = Sort.by(Sort.Direction.ASC,"stuBh");
List<Student> all = stuDAO.findAll(new Specification<Student>() {
    @Override
    public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//              (1) 获取需要查询的属性
        Path<Object> stuUser = root.get("stuUser");
        Predicate like = cb.like(stuUser.as(String.class), "王%");
//              将结果返回
        return like;
    }
},sort);
System.out.println(all);
多条件查询

自定义查询条件

  1. 实现Specification接口(提供泛型:查询的对象类型)
  2. 实现toPredicate方法(构造查询条件)
  3. 需要借助方法参数中的两个参数(
    root:获取需要查询的对象属性
    CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
    )
List<Student> all = stuDAO.findAll(new Specification<Student>() {
@Override
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//              (1)查询的属性
    Path<Object> stuAge = root.get("stuAge");
    Path<Object> stuUser = root.get("stuUser");
//              (2)构造查询方式
//              1、构造stuAge的精准匹配
    Predicate p1 = cb.equal(stuAge, "30");   # 参数一:path(属性名)、参数二: 属性值 
//              2、构造stuUser的精准匹配
    Predicate p2 = cb.equal(stuUser, "张三");
//             (3)、将多个查询条件组合到一起(and表示满足条件一并且要满足条件二)
    Predicate and = cb.and(p1, p2);
//             (4)将结果返回
    return and;
}
});
System.out.println(all);
分页查询
Page<T> findAll (Specification对象, Pageable pageable) ;
/**
*分页查询
*Specification:查询条件
*Pageable对象:分页参数
*分页参数:查询的页码,每页查询的条数
*findAll(Specification, Pageable):带有条件的分页
*findAll(Pageable):没有条件的分页
*返回: Page (springDataJpa为 我们封装好的pageBean对象,数据列表,共条数)
*/

实现

Pageable pageable = PageRequest.of(page,size);
		// page:分页,页码,size:每页数据量
Sort sort = Sort.by(Sort.Direction.ASC,"stuBh");
	 	// sort:分页对象,根据stuBh属性进行分页
Page<Student> all = stuDAO.findAll(new Specification<Student>() {
		// 实现Specification接口,实现动态查询,并分页
    @Override
    public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//              (1) 获取需要查询的字段
        Path<Object> stuUser = root.get("stuUser");
//             (2)拼接条件
        Predicate like = cb.like(stuUser.as(String.class), "王%");
//              将结果返回
        return like;
    }
}, pageable);
System.out.println(all.get());
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值