JPA

Author Avatar
kevin
发表:2023-10-22 14:10:00
修改:2024-10-09 14:10:08

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);
}
评论