jpa 删除是否成功_spring data jpa 实战之增删改查(干货!你想要的查询!)

熟悉了mybatis的灵活,可能你对他的动态查询很喜欢,表示各种sql都能胜任。初步接触jpa时,你会各种吐槽,不如mybatis来的方便。其实jpa也能帮你完成你的各种需求,至于编写的复杂度,那可能就仁者见仁智者见智了。习惯了,其实也一样了。

save操作(含merge操作,即update也在save里)

save方法会预检查该entity是否持久化,isNew会判断该对象的Id类型 是否实现Persistable或EntityInformation进行重写isNew方法,如果Id是Number类型,直接判断value==0 true 执行entityManager.persist 否则执行entityManager.merge()

/**

* save方法会预检查该entity是否持久化,isNew会判断该对象的Id类型 是否实现Persistable或EntityInformation进行

* 重写isNew方法,如果Id是Number类型,直接判断value==0 true 执行entityManager.persist 否则执行entityManager.merge()

*/

@Test

public void insert() {

Customer customer = new Customer();

customer.setName("lk");

customer.setEmail("spring.jpa@163.com");

customer.setAddress("Shanghai PuDong Area XueYe Road");

customer.setPhone("13699999999");

//这里保存以后customer的id会被填充为保存后entity的id

Customer savedEntity = customerRepository.save(customer);

//保存并立即刷新数据库,由于customer以及提供id,会执行merge方法进行保存

// Customer savedAndFlush = customerRepository.saveAndFlush(customer);

List batchCustomers = Arrays.asList(new Customer(), new Customer());

//批量保存,saveAll是循环单挑插入,并不是batch操作,数据较大使用时请注意性能

// List batchSaves = customerRepository.saveAll(batchCustomers);

}

delete操作

delte操作会先执行查询(除了batch操作),在执行删除,若查询不到结果,抛出异常(EmptyResultDataAccessException)不执行删除

/**

* 删除操作,除了batch操作,其他方法均先查询后删除

*/

@Test

public void delete() {

//select * from customer where id=?;delete from customer where id=?;

//同delete(entity)

customerRepository.deleteById(38L);

//select * from customer;循环遍历id单个删除...delete from customer where id=?...

customerRepository.deleteAll();

Customer customer = new Customer();

customer.setId(Long.valueOf(42L));

Customer customerOther = new Customer();

customerOther.setId(41L);

List deleteAll = Arrays.asList(customer,customerOther);

//循环执行delete(entity)

customerRepository.deleteAll(deleteAll);

//不查询直接:delete from customer;(风险较大清空表)

customerRepository.deleteAllInBatch();

//不查询直接:delete from customer where id=? or id=?

customerRepository.deleteInBatch(deleteAll);

}

最常用的query操作

jpa 官方查询关键字

Keyword

Sample

JPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

单表字段查询

//select * from customer;

customerRepository.findAll();

//select * from customer where id = 1;

customerRepository.findById(1L);

//select * from customer where address = "address";

customerRepository.findCustomerByAddress("address");

//select * from customer where name = "lk" and phone = "133";

customerRepository.findCustomersNameAndPhone("133", "lk");

//select * from customer where name like '%k';

customerRepository.findCustomersNameLike("k");

//select * from customer where name like 'k'; 如果需要模糊查询需要手动拼接 % 连接符

customerRepository.findCustomersByNameLike("k");

//select * from customer where name like "%l";

customerRepository.findCustomersByNameStartingWith("l");

//select * from customer where name like "%k%";

customerRepository.findCustomersByNameContains("k");

/ /.....还有很多,不再一一列举......

分页,排序

//select * from customer order by name desc;

customerRepository.findAll(Sort.by(Direction.DESC, "name"));

//select * from customer limit 0,10;

customerRepository.findAll(PageRequest.of(0, 10));

example查询(场景较少)

Customer customer = new Customer();

customer.setAddress("address");

//select * from customer where address ="address";

customerRepository.findAll(Example.of(customer));

customer.setName("lk");

customer.setPhone("133");

ExampleMatcher matcher = ExampleMatcher.matching()

.withMatcher("name", match -> match.contains())

.withMatcher("phone", match -> match.startsWith());

//select * from customer where name like '%lk%" and phone like '133%' and address = "address";

customerRepository.findOne(Example.of(customer, matcher));

namedQuery 也是自定义的@Query的一种

//entity

@Entity(name = "Customer")

@NamedQuery(name = "Customer.findByNameNQ", query = "select c from Customer c where name =?1")

public class Customer {}

//repository定义方法findByNameNQ

List findByNameNQ(String name);

//test

//namedQuery:select * from customer where name = "lk";

customerRepository.findByNameNQ("lk");

@Query自定义JQL语句查询,语法跟sql类似,但注意基于entity的命名,如果属性nativeQuery为ture则,必须用原生sql语句

@Query("select c from Customer c where name like %?1")

List findCustomersNameLike(String name);

@Query("select c from Customer c where name = :name and phone = :phone")

List findCustomersNameAndPhone(@Param("phone") String phone,

@Param("name") String name);

@Query(value = "select * from customer where name =?1",nativeQuery = true)

List findByNameSql(String name);

@Modify 配合 @Query实现 修改部分字段

@Modifying

@Query("update Customer c set c.name = :name where c.id = :id")

int modifyByPhone(@Param("name") String name,@Param("id") Long id);

@Modifying

@Query("delete from Customer c where c.id = ?1")

int deleteCustomer(Long id);

自定义返回值,基于JQL语法,在拼装返回结果集时,是根据构造函数进行组装的,可以基于接口或者类,要保证属性是entity内的属性。也可以借助@Query,使用 new map()返回map,或者new Class返回想要的结果。

//repository

List findCustomersByName(String name);

List findByName(String name);

@Query("select new com.spring.jpa.beans.NameOnly(name,address) from Customer where name = ?1")

List findByName4Obj(String name);

@Query("select new map(name as myname,address as myaddress) from Customer where name = :name")

List> findByName4Map(@Param("name") String name);

//test

//基于接口的返回值

List interfaces = customerRepository.findCustomersByName("lk1");

//基于类的返回值,如果有两个构造函数会报错,无法解析转换

List nameOnlies = customerRepository.findByName("lk1");

//基于类的返回值,@Query显式声明返回bean

List objs = customerRepository.findByName4Obj("lk1");

//@Query返回map 用as做key,不用as默认key是0,1,2...

List> maps = customerRepository.findByName4Map("lk1");

复杂关联关系查询 @OneToOne @ManyToMany @ManyToOne @OneToMany

customer 顾客表,和customer_group 多对一

customer_group 顾客分组表

book 书籍表,customer是多对多。

book_detail 书籍详细表,和book是一对一

四张表没任何业务,假象出来的,单纯为了验证jpa查询方式

表关系注解参数(@OneToOne @ManyToMany @ManyToOne @OneToMany )

Cascade

级联操作

CascadeType. PERSIST

级联持久化 ( 保存 ) 操作

CascadeType. MERGE

级联更新 ( 合并 ) 操作

CascadeType. REFRESH

级联刷新操作,只会查询获取操作

CascadeType. REMOVE

级联删除操作

CascadeType. ALL

级联以上全部操作

Fetch

抓取是否延迟加载,默认情况一的方为立即加载,多的一方为延迟加载,可以手动指定Fetch.EAGER/Fetch.LAZY

mappedBy

关联关系由此方属性维护,可以理解成一对注解使用mappedBy的一方由另一方维护,且必须是注解作用下的属性名。

可以根据关联表的属性作为条件查询,结果同样是根据两次sql查询出来的。通过关联表的属性进行查询时,使用关联 entityName_columnName方式,或者直接使用 _columnName进行查询。如果不想级联查询时,在一方不适用注解即可。

@OneToOne

一对一关联关系,有三种形式存在:

两张表共享主键pk,使用@PrimaryKeyJoinColumn来建立关联关系

@Entity

public class A {

@Id

private Long id;

@OneToOne(cascade = CascadeType.ALL)

@PrimaryKeyJoinColumn

private B b;

}

@Entity

public class B {

@Id

private Long id;

}

通过中间表建立关联关系,使用@JoinTable注解,joinColumns指定本表和关联表的外键,inverseJoinColumns指定关联关系另一方和关联表的外键

@Entity

public class A {

@Id

private Long id;

@OneToOne(cascade = CascadeType.ALL)

joinColumns = @JoinColumn(name="a_fk"),

inverseJoinColumns = @JoinColumn(name="b_fk")

private B b;

}

@Entity

public class B {

@Id

private Long id;

@OneToOne(mappedBy="b")

private A a;

}

通过外键,唯一约束指定关联关系,使用@JoinColumn注解,如果不写该注解,默认会在此表中自动创建连接列:主表属性_关联表主键名称

@Entity(name = "book")

public class Book {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String name;

private Integer count;

@OneToOne(cascade = CascadeType.ALL)

@JoinColumn(name = "detail_id",referencedColumnName = "id")

//referencedColumnName 不写默认是主键,当不是主键是可以用此声明,但必须保证连接键在连接表是唯一约束的

private BookDetail bookDetail;

//setter getter

}

@Entity(name = "book_detail")

public class BookDetail {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String size;

//不需要依赖book可以不写关联属性

//setter getter

}

public interface BookRepository extends JpaRepository {

//可以查出关联表的实体,结果是根据两次sql查询出来的,即select * from book where name = ?;->查出detail_id->select * from book_detail where id = ?;

List findByName(String name);

//可以根据关联表的属性作为条件查询,结果同样是根据两次sql查询出来的。通过关联表的属性进行查询时,使用关联 entityName_columnName方式,或者直接使用 _columnName进行查询。

List findByNameAndBookDetail_Id(String name, Long id);

//自定义返回值,一次查询返回结果

@Query("select new com.spring.jpa.beans.BookResult(b.id as id,b.name as name ,d.size as size) from Book as b left join BookDetail as d on b.bookDetail = d.id")

List findResults();

}

@ManyToMany

通过@ManyToMany 注解定义多对多关系,同时通过 @JoinTable 注解描述关联表和关联条件。

// 维护端注解

@Entity

public class A {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

@ManyToMany (cascade = CascadeType.REFRESH)

@JoinTable (

name = "a_b" , //关联表名

inverseJoinColumns = @JoinColumn (name = "a_id" ),//被维护端外键

joinColumns = @JoinColumn (name = "b_id" ))//维护端外键被维护端注解

private B b;

//setter getter

}

@Entity

public class B {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

@ManyToMany(cascade = CascadeType.REFRESH,

mappedBy = "b",//通过维护端的属性关联

fetch = FetchType.LAZY)

// 关系维护端删除时,如果中间表存在些纪录的关联信息,则会删除该关联信息;

// 关系被维护端删除时,如果中间表存在些纪录的关联信息,则会删除失败 .

private A a;

//setter getter

}

默认joinColumn值:关联表名:主表表名 + 下划线 + 从表表名;关联表到主表的外键:主表表名 + 下划线 + 主表中主键列名;关联表到从表的外键名:主表中用于关联的属性名+ 下划线 + 从表的主键列名

@ManyToOne @OneToMany

注解和上面都差不多,无非就是谁关联谁

@Entity

@NamedQuery(name = "Customer.findByNameNQ", query = "select c from Customer c where name =?1")

public class Customer {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String name;

private String email;

private String address;

private String phone;

// //targetEntity 默认是关联实体,若使用接口作为关联实体时,应指明targetEntity的实现类,且接口应继承Serializable,否则无法被解析

// @ManyToOne(fetch = FetchType.EAGER,targetEntity = CustomerGroup.class)

// @JoinColumn(name = "group_type",referencedColumnName = "type")

// private CustomerGroupInterface customerGroup;

//manyToOne 单向关联或 oneToMany双向关联

// //这里使用非主键作为外键关联,type在customer_group表中唯一约束,也可以使用@JoinTable 处理关联表做连接这里不再演示

@ManyToOne(fetch = FetchType.EAGER,cascade = CascadeType.ALL)

@JoinColumn(name = "group_type",referencedColumnName = "type")

private CustomerGroup customerGroup;

//customerGroup为一的一方,单向关联customer(多方)

// @Column(name = "group_type")

// private String type;

//setter getter

}

@Entity

@Table(name = "customer_group")

public class CustomerGroup implements CustomerGroupInterface {

private static final long serialVersionUID = -6956725658881048590L;

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

@Column(unique = true)

private String type;

private String name;

private Integer level;

//oneToMany 单向关联,customer中无需注解,注意joinColumn是table中的name

// @OneToMany(fetch = FetchType.EAGER)

// @JoinColumn(name = "group_type")

//oneToMany 双向关联,customer中无需注解,mappedBy必须指向entity中的属性名,即标有@ManyToOne的属性名,且此处不可再使用@JoinColumn。

@OneToMany(fetch = FetchType.EAGER, mappedBy = "customerGroup")

private List customers;

//setter getter

}

其他注解

注解

解释

@Entity

声明一个类为实体Bean。

@Table

说明此实体类映射的表名,目录,schema的名字。

@Id

声明此表的主键。

@GeneratedValue

定义主键的增长策略。我这里一般交给底层数据库处理,所以调用了名叫generator的增长方式,由下边的@GenericGenerator实现。

@GenericGenerator

hibernate内部的主键增长方式。

@Version

注解用于支持乐观锁版本控制。一般可以用 数字 或者 timestamp 类型来支持 version.

@Column

name 可选,列名(默认值是属性名); unique 可选,是否在该列上设置唯一约束(默认值false);nullable 可选,是否设置该列的值可以为空(默认值true); insertable 可选,该列是否作为生成的insert语句中的一个列(默认值true);updatable 可选,该列是否作为生成的update语句中的一个列(默认值true); columnDefinition 可选,为这个特定列覆盖SQL DDL片段 (这可能导致无法在不同数据库间移植); table 可选,定义对应的表(默认为主表);length 可选,列长度(默认值255);precision 可选,列十进制精度(decimal precision)(默认值0);scale 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0)

@Index

某一字段加索引 @Table(name = "customer", indexes = {@Index(columnList = "name")}),给name字段加上索引

@Transient

被注解成 @Transient 的 getter 方法或属性,将不会被持久化(自己测试,只有放在getter方法内才起作用)

@Basic

所有没有定义注解的属性,等价于在其上面添加了 @Basic注解可以声明属性的获取策略 ( fetch strategy ),fetch:抓取策略,延时加载与立即加载,optional:指定在生成数据库结构时字段是否允许为 null.

@Temporal

在核心的 Java API 中并没有定义时间精度 ( temporal precision )。因此处理时间类型数据时,你还需要定义将其存储在数据库中所预期的精度。

@Enumerated

枚举类型成员属性映射,EnumType.STRING指定属性映射为字符串,EnumType.ORDINAL指定属性映射为数据序

@Lob

用于标注字段类型为Clob和Blob类型,Clob(Character Large Ojects)类型是长字符串类型,实体的类型可为char[]、Character[]、或者String类型,Blob(Binary Large Objects)类型是字节类型,实体的类型可为byte[]、Byte[]、或者实现了Serializable接口的类。通常使用惰性加载的方式,@Basic(fetch=FetchType.LAZY)

@SecondaryTable

(@javax.persistence.SecondaryTable)将一个实体映射到多个数据库表中

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值