JPA
jpa使用的是ORM规范,底层使用的是Hibernate实现
ORM(Object-Relational Mapping) 表示对象关系映射
ORM就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。
可以设置自动创建表
版本区别
在springboot2.0和2.2以上,代码有些区别
其中包括,mysql版本升级到8.0
必须在url中添加时区serverTimezone=Asia/Shanghai
驱动必须是cj驱动 com.mysql.cj.jdbc.Driver
jpa增删改等有些代码变化
需要启动类
jpa入门案例
工程spring_data_jpa
设置配置文件,resource文件夹下面添加一个 application.yml
的配置文件
logging:
level:
com.yee: debug # 日志级别
spring:
datasource: #数据源
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver # 8.0以后使用cj和时区
url: jdbc:mysql://127.0.0.1:3306/spring_jpa?serverTimezone=Asia/Shanghai
jpa:
database: mysql #使用的数据库
show-sql: true #是否控制台显示sql语句
generate-ddl: true # 是否自动创建表
hibernate:
ddl-auto: update #自动更新
server:
port: 8888
创建实体类
/**
* ClassName: pojo
* Description:
* date: 2021/12/13 9:34
*所有的注解都是使用JPA的规范提供的注解,
* 所以在导入注解包的时候,一定要导入javax.persistence下的
* @author Yee
* @since JDK 1.8
*/
@Entity //表示该类就是一个持久化对象,与数据库表建立的映射
@Table(name = "cst_customer") //建立实体类和表的映射关系
public class User implements Serializable {
@Id //注解id
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键策略自生长
@Column(name = "cust_id") //指定和表中cust_id字段的映射关系
private Long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_address")
private String custAddress;
@Column(name = "cust_phone")
private String custPhone;
//配置客户和联系人的一对多关系
//CascadeType.ALL:表示所有操作,增删改等
@OneToMany(cascade = CascadeType.ALL)
private Set<LinkMan> linkmans = new HashSet<>();
...省略getset方法
创建dao接口
继承JpaRepository之后,默认有个代理类已经实现了大部分方法
只需要按照框架的规范提供dao接口,不需要实现类就可以完成数据库的增删改查、分页查询等方法的定义,极大的简化了我们的开发过程。
/**
* ClassName: UserDao
* Description:
* date: 2021/12/13 10:53
* 实现JpaRepository,jpa的一个规范
* 第一个参数是实体类,第二个参数是id的类型
*
* @author Yee
* @since JDK 1.8
*/
public interface UserDao extends JpaRepository<User,Long> {}
入门小案例
根据id进行查询
必须在get一次否则返回的是optional这个类
注入dao层
@Autowired
private UserDao userDao;
//根据ID查询用户
User user = userDao.findById(1L).get();
System.out.println(user);
保存和更新
根据id来判断增加还是更新,使用的是同一个方法,sava
// 保存用户
User user = new User(null,"张三","测试","哈哈","嘻嘻","呵呵","手机号");
userDao.save(user);
//根据id更新用户
User user = new User(1L,"张三","测试","哈哈","嘻嘻","呵呵","手机号");
userDao.save(user);
排序
/**
* 新版排序不在使用new的方式构造sort
* 构造器私有化,只能通过by方法获得sort实例
*/
@Test
public void userSort(){
//通过id排序
Sort custId = Sort.by(Sort.Direction.DESC, "custId");
List<User> all = userDao.findAll(custId);
for (User user : all) {
System.out.println(user);
}
}
分页查询
/**
* 新版分页,不在使用new方法
* 使用of方法获取实例
*/
@Test
public void pageUser(){
//分页,第一个参数表示从哪页开始,第二个参数表示每页几条数据
PageRequest request = PageRequest.of(0, 2);
Page<User> all = userDao.findAll(request);
System.out.println("总记录数:"+all.getTotalElements());
System.out.println("总页数:"+all.getTotalPages());
//分页的内容
List<User> content = all.getContent();
for (User user : content) {
System.out.println(user);
}
}
JPQL复杂查询
常见的查询
User是实体类,?后面的数字表示参数索引
@Query默认表示查询
要执行增删改的操作,在@Query注解的下面添加:@Modifying
//查询所有,默认使用jpql语句,
@Query("from User")
public List<User> findAllUser();
//根据名字查询,新版不能直接like,concat是连接符,
@Query("from User where custName like concat('%',?1,'%') ")
public List<User> findCustNameLike(String custName);
// 根据多个条件进行查询
//模糊查询,同时按照客户所属行业匹配查询
@Query("from User where custName like concat('%',?1,'%') and custIndustry like concat('%',?2,'%') ")
public List<User> findCustNameLikeAndCustIndustry(String custName,String custIndustry);
//更新,@Query默认表示查询,如果要执行增删改的操作,在@Query注解的下面添加:@Modifying
@Query("update User set custAddress = ?1 where custId = ?2 ")
@Modifying
public void updateUserCustAddress(String custAddress,Long custId);
开启原生查询
@Query参数nativeQuery = true
@Query(value = "select * from cst_customer",nativeQuery = true)
List<Customer> findAllCustomerSQL();
@Query(value = "select * from cst_customer where cust_name like ?",nativeQuery = true)
List<Customer> findCustNameLikeSQL(String custName);
@Query(value = "select * from cst_customer where cust_name like ? and cust_industry = ?",nativeQuery = true)
List<Customer> findCustNameLikeAndCustIndustrySQL(String custName, String custIndustry);
方法命名规则查询
命名规则语法:
- findBy开头
- 属性名称(首字母大写) -- 默认Is
- 查询的关键字(首字母大写)
- Like
- LessThan 小于等于
- Between 介于…之间
- Equal 等于
- 连接多条件的关键字(首字母大写)
- And Or
//方法命名规则查询
//基本查询,按名称查询
public List<User> findByCustName(String custName);
// 客户名称模糊
public List<User> findByCustNameLike(String custName);
// 多条件查询,保证第1个参数就是方法命名的第一个字段
public List<User> findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
一对多关系
级联操作指操作一个对象同时操作它的关联对象
CascadeType.ALL:表示所有
CascadeType.MERGE:表示更新
CascadeType.PERSIST:表示保存
CascadeType.REMOVE:表示删除
User类上添加
//配置客户和联系人的一对多关系
//CascadeType.ALL:表示所有操作,增删改等
@OneToMany(cascade = CascadeType.ALL)
private Set<LinkMan> linkmans = new HashSet<>();
联系人类
@Entity
@Table(name="cst_linkman")
public class LinkMan implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="lkm_id")
private Long lkmId;
@Column(name="lkm_name")
private String lkmName;
@Column(name="lkm_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;
//多对一关系映射:多个联系人对应客户
@ManyToOne(targetEntity = User.class) //目标类
//name当前类的外键字段,第二个参数,引用的主键字段名称
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private User user;
}
级联操作
/**
* 存储用户的同时,会一起把联系人存储进去
*/
@Test
@Transactional
@Rollback(false)
public void testAdd(){
User user = new User();
user.setCustName("我是用户名");
user.setCustLevel("VIP");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("我是联系人");
//由于是级联保存,所以需要互相存储
linkMan.setUser(user);
user.getLinkmans().add(linkMan);
userDao.save(user);
}
/**
* 级联删除
*/
@Test
@Transactional
@Rollback(false)
public void testDelete(){
Optional<User> optional = userDao.findById(5L);
//先判断有没有,有就获取,否则就是null,直接用如果是没有会报异常
User user = optional.isPresent() ? optional.get() : null;
userDao.delete(user);
}
多对多关系
多对多的表关系建立靠的是中间表,其中用户表和中间表的关系是一对多,角色表和中间表的关系也是一对多
其中一方需要放弃配置权,否则会冲突,主键重复@ManyToMany(mappedBy = "roles")
/**
* ClassName: Master
* Description:
* date: 2021/12/13 19:06
* 多对多关系
* @author Yee
* @since JDK 1.8
*/
@Entity
@Table(name = "sys_user")
public class Master implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="user_id")
private Long userId;
@Column(name="user_name")
private String userName;
@Column(name="age")
private Integer age;
//配置多对多的关系,第二个参数表示级联操作
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
//name中间表名字,joinColumns配置中间表外键,inverseJoinColumns配置引用的另一个表
@JoinTable(name = "sys_user_role",joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")})
private Set<Role> roles = new HashSet<>();
}
角色表
@Entity
@Table(name = "sys_role")
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
//配置多对多
@ManyToMany(mappedBy = "roles")
private Set<Master> users = new HashSet<>();
}
级联操作
//测试级联添加(保存一个用户的同时保存用户的关联角色)
@Test
@Transactional
@Rollback(false)
public void testCasCade(){
Master master = new Master();
master.setUserName("小王");
Role role = new Role();
role.setRoleName("java程序员");
//配置用户和角色的关系
master.getRoles().add(role);
role.getUsers().add(master);
masterDao.save(master);
}
//删除id为1的用户,同时删除他的关联对象
@Test
@Transactional
@Rollback(false)
public void testRemove(){
//查询1号用户
Master master = masterDao.findById(1L).get();
masterDao.delete(master);
}