熟悉了mybatis的灵活,可能你对他的动态查询很喜欢,表示各种sql都能胜任。初步接触jpa时,你会各种吐槽,不如mybatis来的方便。其实jpa也能帮你完成你的各种需求,至于编写的复杂度,那可能就仁者见仁智者见智了。习惯了,其实也一样了。
* save方法会预检查该entity是否持久化,isNew会判断该对象的Id类型 是否实现Persistable或EntityInformation进行
* 重写isNew方法,如果Id是Number类型,直接判断value==0 true 执行entityManager.persist 否则执行entityManager.merge()
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 官方查询关键字
KeywordSampleJPQL snippet
findByLastnameAndFirstname
… where x.lastname = ?1 and x.firstname = ?2
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
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
findByLastnameNot
… where x.lastname <> ?1
findByAgeIn(Collection<Age> ages)
… where x.age in ?1
NotIn
findByAgeNotIn(Collection<Age> ages)
… where x.age not in ?1
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");
/ /.....还有很多,不再一一列举......
分页,排序
customerRepository.findAll(Sort.by(Direction.DESC, "name"));
customerRepository.findAll(PageRequest.of(0, 10));
example查询(场景较少)
Customer customer = new Customer();
customer.setAddress("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());
customerRepository.findOne(Example.of(customer, matcher));
namedQuery 也是自定义的@Query的一种
@Entity(name = "Customer")
@NamedQuery(name = "Customer.findByNameNQ", query = "select c from Customer c where name =?1")
public class Customer {}
List<Customer> findByNameNQ(String name);
customerRepository.findByNameNQ("lk");
@Query自定义JQL语句查询,语法跟sql类似,但注意基于entity的命名,如果属性nativeQuery为ture则,必须用原生sql语句
@Query("select c from Customer c where name like %?1")
List<Customer> findCustomersNameLike(String name);
@Query("select c from Customer c where name = :name and phone = :phone")
List<Customer> findCustomersNameAndPhone(@Param("phone") String phone,
@Param("name") String name);
@Query(value = "select * from customer where name =?1",nativeQuery = true)
List<Customer> 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返回想要的结果。
List<NameOnlyI> findCustomersByName(String name);
List<NameOnly> findByName(String name);
@Query("select new com.spring.jpa.beans.NameOnly(name,address) from Customer where name = ?1")
List<NameOnly> findByName4Obj(String name);
@Query("select new map(name as myname,address as myaddress) from Customer where name = :name")
List<Map<String, Object>> findByName4Map(@Param("name") String name);
List<NameOnlyI> interfaces = customerRepository.findCustomersByName("lk1");
List<NameOnly> nameOnlies = customerRepository.findByName("lk1");
List<NameOnly> objs = customerRepository.findByName4Obj("lk1");
List<Map<String, Object>> maps = customerRepository.findByName4Map("lk1");
复杂关联关系查询 @OneToOne @ManyToMany @ManyToOne @OneToMany
customer 顾客表,和customer_group 多对一
customer_group 顾客分组表
book 书籍表,customer是多对多。
book_detail 书籍详细表,和book是一对一
四张表没任何业务,假象出来的,单纯为了验证jpa查询方式
表关系注解参数(@OneToOne @ManyToMany @ManyToOne @OneToMany )
Cascade级联操作
抓取是否延迟加载,默认情况一的方为立即加载,多的一方为延迟加载,可以手动指定Fetch.EAGER/Fetch.LAZY
mappedBy
关联关系由此方属性维护,可以理解成一对注解使用mappedBy的一方由另一方维护,且必须是注解作用下的属性名。
可以根据关联表的属性作为条件查询,结果同样是根据两次sql查询出来的。通过关联表的属性进行查询时,使用关联 entityName_columnName方式,或者直接使用 _columnName进行查询。如果不想级联查询时,在一方不适用注解即可。
@OneToOne
一对一关联关系,有三种形式存在:
两张表共享主键pk,使用@PrimaryKeyJoinColumn来建立关联关系
@Entity
public class A {
private Long id;
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
private B b;
@Entity
public class B {
private Long id;
通过中间表建立关联关系,使用@JoinTable注解,joinColumns指定本表和关联表的外键,inverseJoinColumns指定关联关系另一方和关联表的外键
@Entity
public class A {
private Long id;
@OneToOne(cascade = CascadeType.ALL)
joinColumns = @JoinColumn(name="a_fk"),
inverseJoinColumns = @JoinColumn(name="b_fk")
private B b;
@Entity
public class B {
private Long id;
@OneToOne(mappedBy="b")
private A a;
通过外键,唯一约束指定关联关系,使用@JoinColumn注解,如果不写该注解,默认会在此表中自动创建连接列:主表属性_关联表主键名称
@Entity(name = "book")
public class Book {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer count;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "detail_id",referencedColumnName = "id")
private BookDetail bookDetail;
@Entity(name = "book_detail")
public class BookDetail {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String size;
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByName(String name);
List<Book> 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<BookResult> findResults();
@ManyToMany
通过@ManyToMany 注解定义多对多关系,同时通过 @JoinTable 注解描述关联表和关联条件。
@Entity
public class A {
@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;
@Entity
public class B {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany(cascade = CascadeType.REFRESH,
mappedBy = "b",
fetch = FetchType.LAZY)
private A a;
默认joinColumn值:关联表名:主表表名 + 下划线 + 从表表名;关联表到主表的外键:主表表名 + 下划线 + 主表中主键列名;关联表到从表的外键名:主表中用于关联的属性名+ 下划线 + 从表的主键列名
@ManyToOne @OneToMany
注解和上面都差不多,无非就是谁关联谁
@Entity
@NamedQuery(name = "Customer.findByNameNQ", query = "select c from Customer c where name =?1")
public class Customer {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String address;
private String phone;
@ManyToOne(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
@JoinColumn(name = "group_type",referencedColumnName = "type")
private CustomerGroup customerGroup;
@Entity
@Table(name = "customer_group")
public class CustomerGroup implements CustomerGroupInterface {
private static final long serialVersionUID = -6956725658881048590L;
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String type;
private String name;
private Integer level;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "customerGroup")
private List<Customer> customers;
注解解释
@Entity
声明一个类为实体Bean。
@Table
说明此实体类映射的表名,目录,schema的名字。
声明此表的主键。
@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指定属性映射为数据序
用于标注字段类型为Clob和Blob类型,Clob(Character Large Ojects)类型是长字符串类型,实体的类型可为char[]、Character[]、或者String类型,Blob(Binary Large Objects)类型是字节类型,实体的类型可为byte[]、Byte[]、或者实现了Serializable接口的类。通常使用惰性加载的方式,@Basic(fetch=FetchType.LAZY)
@SecondaryTable
(@javax.persistence.SecondaryTable)将一个实体映射到多个数据库表中