SpringData
orm思想
-
主要的目的:操作实体类就完成操作数据库表
-
建立两个映射关系:
- 实体类和表的映射关系
- 实体类中的属性和表中的字段关系
-
不再将重点放在sql语句上
-
实现orm思想的框架有:Mybatis、Hibernate
Hibernate框架
- 百度百科: Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用。
JPA的基本操作
配置jpa的核心配置文件
- 位置:类路径下
META-INF
的文件夹下 - 命名:
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--
配置persistence-unit节点
name:持久化单元名称
transaction-type:事务管理方式
- JTA:分布式事务管理
- RESOURCE_LOCAL :本地事务管理
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!--
数据库信息:
-用户名: javax.persistence.jdbc.user
-密码 : javax.persistence.jdbc.password
-驱动 : javax.persistence.jdbc.driver
-数据库地址 : javax.persistence.jdbc.url
-->
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="123456"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
<!--
配置jpa实现方(hibernate)的配置信息:
-显示sql :false|true
-自动创建数据库表 :hibernate.hbm2ddl.auto
1.create :程序运行时创建数据库库表
2.update :程序运行时创建表
3none :不会创建表
-->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
实体类
package com.lxs.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
/**
* 客户的实体类
* 配置映射关系:
* 1.实体类和表的映射关系:
*
* @Entity:声明实体类
* @Table :配置实体类和表的映射关系
* - name:数据库表名
* 2.实体类和表中的字段的映射关系
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "cst_customer")
public class Customer {
/**
* @Id:声明主键的配置
* @GeneratedValue -strategy = GenerationType.IDENTITY:自增
* @Column name:数据库中字段的名称
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custID;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_phone")
private String custPhone;
@Column(name = "cust_address")
private String custAddress;
}
工具类
package com.lxs.Utils;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* 解决实体类工厂耗时和浪费时间的问题
* 通过静态代码块的形式
*/
public class JpaUtils {
private static EntityManagerFactory factory;
static {
factory = Persistence.createEntityManagerFactory("myJpa");
}
public static EntityManager getEntityManager(){
return factory.createEntityManager();
}
}
测试类(包含增删改查)
import com.lxs.Utils.JpaUtils;
import com.lxs.pojo.Customer;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaTest {
/**
* 测试jpa
* 实现目标:保存一个客户到数据库中
* 操作步骤:
* 1.加载配置文件创建工厂(实体管理器工厂)对象
* 2.通过工厂获取实体管理器
* 3.获取事务对象,开启事务
* 4.完成增删改操作
* 5.提交事务(回滚)
* 6.释放资源
*/
@Test
public void TestJpa(){
// // 1.加载配置文件创建工厂(实体管理器工厂)对象
// EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
// 2.通过工厂获取实体管理器
EntityManager em = JpaUtils.getEntityManager();
//3.获取事务对象,开启事务
EntityTransaction tx = em.getTransaction();
//4.开启事务
tx.begin();
//5. 完成增删改操作,存入客户
Customer customer=new Customer();
customer.setCustName("haha");
customer.setCustIndustry("java");
//保存
em.persist(customer);
//提交事务
tx.commit();
//释放资源(倒着释放)
em.close();
// factory.close();
}
/**
* 测试根据id查询数据
* Find: 有两个参数:
* -class:想要返回的实体类对应类型的字节码
* -id 主键id
*
*/
@Test
public void TestFind(){
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer customer = entityManager.find(Customer.class, 1l);
System.out.println(customer);
tx.commit();
entityManager.close();
}
/**
* 测试根据id查询数据
* 延迟加载(懒加载),一般常用这个
* -得到的是一个动态代理对象
* -什么时候用什么时候查询
*/
@Test
public void TestGetReference(){
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
Customer customer = entityManager.getReference(Customer.class, 1l);
System.out.println(customer);
tx.commit();
entityManager.close();
}
/**
* 根据id删除数据
* remove方法
* entityManager.remove(Object o)
*
* 先根据id查询到对应的对象
* 然后删除对象
*/
@Test
public void TestRemove(){
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//查询
Customer customer = entityManager.getReference(Customer.class, 1l);
//删除
entityManager.remove(customer);
tx.commit();
entityManager.close();
}
/**
* 更新操作
* entityManager.merge(customer);
*
*/
@Test
public void TestMerge(){
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//查询对象
Customer customer = entityManager.getReference(Customer.class, 1l);
//更新操作
customer.setCustAddress("华为");
//保存
entityManager.merge(customer);
tx.commit();
entityManager.close();
}
}
Jpql
查询全部
-
jpql查询全部的语句
from 表对应的实体类对象(from com.lxs.pojo.Customer)
-
sql 语句
select * from 表名
import com.lxs.Utils.JpaUtils;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import java.util.List;
public class JpqlTest {
@Test
public void testFindAll(){
//获取EntityManager对象
EntityManager em = JpaUtils.getEntityManager();
//开启事务
EntityTransaction transaction = em.getTransaction();
transaction.begin();
//查询全部
String jpql="from Customer";
Query query = em.createQuery(jpql);
//发送查询,封装结果集
List list = query.getResultList();
for (Object o : list) {
System.out.println(o);
}
//提交事务
transaction.commit();
//释放资源
em.close();
}
}
倒叙查询
from Customer order by custID(表中主键对应的实体类属性) desc;
//查询个数
String jpql="from Customer order by custID desc";
Query query = em.createQuery(jpql);
//发送查询,返回单个结果集
Object result = query.getSingleResult();
System.out.println(result);
分页查询
- 先查询全部,然后再对参数进行赋值jpql语句是查询全部的语句
//查询全部
String jpql="from Customer";
Query query = em.createQuery(jpql);
//对参数进行赋值(分页参数)
//起始索引
query.setFirstResult(0);
//每页个数
query.setMaxResults(2);
//发送查询,封装结果集
List list = query.getResultList();
条件查询
from 表对应的实体类名称 where 条件 like ?
(from Customer where custID like ?)
//根据Id查询
String jpql = "from Customer where custID like ?";
Query query = em.createQuery(jpql);
//对参数进行赋值(占位符参数)这个是从1开始不是从0
query.setParameter(1, 2l);
//发送查询,封装结果集
List list = query.getResultList();
SpringDataJPA
前言:学了上面的,之后我们发现上面的代码依然会有很多的冗余,所以这时候就引入了SpringDataJPA框架
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ztgKPD26-1604499411995)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\1603951560380.png)]
搭建环境
- 首先我们需要导入的依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
- 编写spring jpa 配置类文件
#配置jpa
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
#连接池配置
spring.datasource.url=jdbc:mysql:///jpa?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
-
配置实体类和表的映射关系
package com.lxs.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.persistence.*; /** * 客户的实体类 * 配置映射关系: * 1.实体类和表的映射关系: * * @Entity:声明实体类 * @Table :配置实体类和表的映射关系 * - name:数据库表名 * 2.实体类和表中的字段的映射关系 */ @Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "cst_customer") public class Customer { /** * @Id:声明主键的配置 * @GeneratedValue -strategy = GenerationType.IDENTITY:自增 * @Column name:数据库中字段的名称 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "cust_id") private Long custID; @Column(name = "cust_name") private String custName; @Column(name = "cust_source") private String custSource; @Column(name = "cust_level") private String custLevel; @Column(name = "cust_industry") private String custIndustry; @Column(name = "cust_phone") private String custPhone; @Column(name = "cust_address") private String custAddress; }
编写一个符合SpringDataJpa 的dao层接口
-
只需要写dao曾接口,不需要编写dao层接口实现类
-
dao层接口规范:继承两个接口
JpaRepository<Customer,Long>
JpaSpecificationExecutor<Customer>
package com.lxs.dao; import com.lxs.pojo.Customer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface CustomerDao extends JpaRepository<Customer,Long>,JpaSpecificationExecutor<Customer>{ }
增删改查
package com.lxs;
import com.lxs.dao.CustomerDao;
import com.lxs.pojo.Customer;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.Optional;
@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootJpaApplicationTests {
@Autowired
private CustomerDao customerDao;
@Test
void contextLoads() {
}
/**
* 测试根据id查询
* 底层 调用em.find方法
* 立即加载
*/
@Test
public void testFindById(){
Optional<Customer> customer = customerDao.findById(2l);
System.out.println(customer);
}
/**
* 测试getOne方法(也是根据id查询)
* 底层调用的是 em.getReference方法
* 懒加载(延迟加载)
* @Transactional 加上这个注解,防止报错
*
*/
@Test
@Transactional
public void testGetOne(){
Customer one = customerDao.getOne(4L);
System.out.println(one);
}
/**
* 测试删除
*/
@Test
public void testDeleteById(){
customerDao.deleteById(2l);
System.out.println("删除成功");
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<Customer> all = customerDao.findAll();
for (Customer customer : all) {
System.out.println(customer);
}
}
/***
*测试数据的总数
*/
@Test
public void testCount(){
long count = customerDao.count();
System.out.println(count);
}
/**
* 测试添加数据,
* 如果设置了id则执行更新操作(但是!!!!!! 如果不先查询直接修改,其他字段会为空。所以我们一般是先查出来后修改)
*
*/
@Test
public void testSave(){
Customer customer=new Customer();
customer.setCustAddress("alibaba");
customer.setCustName("aaa");
customer.setCustLevel("88");
customerDao.save(customer);
}
/***
*
* 测试更新
*/
@Test
public void testUpdate(){
Optional<Customer> customer = customerDao.findById(1l);
Customer customer1 = customer.get();
customer1.setCustName("我修改");
customerDao.save(customer1);
}
/**
*
* 判断id为4的客户是否存在
*/
@Test
public void testExistsById(){
boolean b = customerDao.existsById(4l);
System.out.println(b);
}
}
SpringData jpql查询
基本操作
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
/**
* 根据客户名称查询客户
* 使用jpql的形式查询
* jpql:from Customer where custName = ?
* 配置jpql语句,使用的@Query注解
*/
@Query("from Customer where custName = ?1")
Customer findJpql(String custName);
/**
* 根据客户名称和客户id查询客户
* jpql: from Customer where custName = ? and custId = ?
* 对于多个占位符参数
* 赋值的时候,默认的情况下,占位符的位置需要和方法参数中的位置保持一致
* 也可以指定占位符参数的位置
* ?索引的方式,指定此占位的取值来源
*/
@Query("from Customer where custName = ?2 and custId = ?1")
Customer findCustNameAndId(Long id, String name);
/**
* 使用jpql完成更新操作
* 根据id更新,客户的名称
* sql :update cst_customer set cust_name = ? where cust_id = ?
* pql : update Customer set custName = ? where custId = ?
* @Query : 代表的是进行查询
* @Modifying : 声明此方法是用来进行更新操作
*/
@Query(value = "update Customer set custName = ?1 where custId = ?2")
@Modifying
@Transactional // springdata jpa使用jpql执行插入,更新,删除需要手动提交事务
@Rollback(false) // 默认在执行之后,回滚事务,这里设置不回滚
void updateCustomer(String custName, long custId);
/**
* 使用sql的形式查询:查询全部的客户
* sql:select * from cst_customer
* nativeQuery : true 使用本地sql的方式查询 false(默认) jpql查询
*/
@Query(value = "select * from cst_customer", nativeQuery = true)
List<Customer> findSql();
使用jpa规范命名
/**
* JPA方法命名规则查询
* 根据客户名称查询
*/
Customer findByCustName(String custName);
/**
* 根据客户名称模糊查询
* @param custName
* @return
*/
List<Customer> findByCustNameLike(String custName);
/**
* 使用客户名称模糊匹配和客户所属行业精准匹配的查询
*/
Customer findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
specification 动态查询
首先,我们看一下JpaSpecificationExcutor中的方法
/**
*这个是查询单个对象
*
*/
1. Optional<T> findOne(@Nullable Specification<T> spec);
/**
*这个是查询列表
*
*/
2. List<T> findAll(@Nullable Specification<T> spec);
/**
*这个是查询所有但是可以分页
*Pageable:页参数
*
*/
3. Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
/**
*这个是查询列表
* sort :排序参数
*
*/
4. List<T> findAll(@Nullable Specification<T> spec, Sort sort);
/**
* 统计查询
*/
5. long count(@Nullable Specification<T> spec);
- Specification:查询条件
自定义实现类:
toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder)
下面是我自己测试的代码:
根据某个属性进行查询
@RunWith(SpringRunner.class)
@SpringBootTest
public class specTest {
@Autowired
private CustomerDao customerDao;
/**
* 实现Specification接口 提供泛型
* 实现 toPredicate方法 构造查询条件
* 需要借助方法中的两个参数
* root 获取需要查询的对象的属性
* CriteriaBuilder 构造查询条件的,内部封装了很多的查询条件(模糊、精准匹配)
*/
@Test
public void testSpec() {
Specification<Customer> specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
//获取比较的属性
Path<Object> custName = root.get("custName");
//构造查询条件
/**
* 第一个参数:需要比较的属性
* 第二个参数:需要比较的属性的取值
*/
Predicate predicate = criteriaBuilder.equal(custName, "我修改");
return predicate;
}
};
Optional<Customer> one = customerDao.findOne(specification);
System.out.println(one);
}
根据多个属性进行查询
@Test
public void testSpecMore() {
Specification<Customer> Specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
//获取比较的属性
Path<Object> custName = root.get("custName");
Path<Object> custAddress = root.get("custAddress");
//构造查询条件
Predicate equal = criteriaBuilder.equal(custName, "测试的");
Predicate equal1 = criteriaBuilder.equal(custAddress, "阿胡");
Predicate and = criteriaBuilder.and(equal, equal1);
return and;
}
};
Optional<Customer> one = customerDao.findOne(Specification);
System.out.println(one);
}
模糊查询+排序
- 里面的Sort类需要使用Sort.by
/**
* 模糊查询
*/
@Test
public void testSpecLike() {
Specification<Customer> specification = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Path<Object> custName = root.get("custName");
Predicate like = criteriaBuilder.like(custName.as(String.class), "测%");
return like;
}
};
/**
* 排序
*/
Sort sort = Sort.by(Sort.Direction.DESC, "custID");
List<Customer> all = customerDao.findAll(specification, sort);
for (Customer customer :
all) {
System.out.println(customer);
}
}
分页
@Test
public void testSpecPage() {
//无条件的排序
Specification specification=null;
Pageable pageable=PageRequest.of(0,2);
Page all = customerDao.findAll(specification, pageable);
System.out.println(all.getTotalElements());//得到总条数
System.out.println(all.getTotalPages());//得到总页数
System.out.print(all.getContent());//得到数据的集合列表
}
多表操作
说之前先说个点: 我用lombok测试jpa级联添加的时候,一直报回滚事务的错误 ,然后换成get、set之后就好了
不废话直接上代码
- 客户实体类
@Entity
@Table(name = "cst_linkman")
public class LinkMan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键
@Column(name = "lkm_id")
private Long lkmId;
@Column(name = "lkm_name")
private String lkmName;
@Column(name = "lkn_gender")
private String lkmGender;
@Column(name = "lkm_phone")
private String lkmPhone;
@Column(name = "lkm_mobile")
private String lkmMobile;
@Column(name = "lkm_email")
private String lkmEmail;
@Column(name = "lkm_position")
private String lkmPosition;
@Column(name = "lkm_memo")
private String lkmMemo;
/**
* 配置联系人到客户的多对一的关系
* 使用注解来配置多对一的关系
* 1.配置表关系
* @ManyToOne
* 2.配置中间表
* @JoinColumn(name = "cst_linkman_id", referencedColumnName ="cust_id")
*/
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id", referencedColumnName ="cust_id")
private Customer customer;
-
主表
@Entity @Table(name = "cst_customer") public class Customer { /** * @Id:声明主键的配置 * @GeneratedValue -strategy = GenerationType.IDENTITY:自增 * @Column name:数据库中字段的名称 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "cust_id") private Long custID; @Column(name = "cust_name") private String custName; @Column(name = "cust_source") private String custSource; @Column(name = "cust_level") private String custLevel; @Column(name = "cust_industry") private String custIndustry; @Column(name = "cust_phone") private String custPhone; @Column(name = "cust_address") private String custAddress; //配置客户和联系人的一对多关系 /** * 使用注解的形式配置多表关系 * 1.声明关系 * @OneToMany配置一对多关系 * targetEntity:对方的字节码对象 * 2.配置外键 (中间表的) * @JoinColumn:配置外键 * name:外键的名称 * referencedColumnName:参照着主表的主键字段名称 * */ // @OneToMany(targetEntity = LinkMan.class) // @JoinColumn(name = "lkm_cust_id",referencedColumnName ="cust_id" ) /** * 上面的备注掉是放弃了外键的维护 * 放弃之后指明customer 来完成这个任务 */ @OneToMany(mappedBy = "customer" ,cascade = CascadeType.ALL) private Set<LinkMan> linkMans=new HashSet<>();
-
测试类
@RunWith(SpringRunner.class) @SpringBootTest public class OneToManyTest { @Autowired private CustomerDao customerDao; @Autowired private LinkManDao linkManDao; @Test @Transactional//配置事务 @Rollback(false)//不回滚 public void testAdd() { Customer customer = new Customer(); customer.setCustName("百度"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("小ha"); customerDao.save(customer); //配置了多对一的关系,当保存的时候就对 linkMan.setCustomer(customer); linkManDao.save(linkMan); } /** * 测试级联参加:保存客户同时保存所有联系人 * 我们需要在操作主体的实体类上配置 casacde 属性 */ @Test @Transactional//配置事务 @Rollback(false)//不回滚 public void testAdd1() { Customer customer = new Customer(); customer.setCustName("百度"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("小sen"); linkMan.setCustomer(customer); customer.getLinkMans().add(linkMan); customerDao.save(customer); } @Test @Transactional//配置事务 @Rollback(false)//不回滚 public void testRemove() { customerDao.deleteById(15l); } }
-