【JPA】mybatis、mybatisPlus、JPA使用过程

JPA

mybatisPlus

mybatis

JPA

Spring Data JPA为Java Persistence API(JPA)提供了实现。它简化了通过JPA访问数据库的开发工作,提供了很多CRUD的快捷操作,还提供了如分页、排序、复杂查询、自定义查询(JPQL)等功能,Spring Data JPA底层也是依赖于Hibernate来实现的,Spring Data JPA拥有标准化、简单易用、面向对象等优势,并且Spring将EntityManager 的创建与销毁、事务管理等代码抽取出来,并由Spring统一进行管理。

引入依赖

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

application.yml配置

server:
#指定服务端口号
port: 8008

spring:
    application:
        #应用名称
        name: spring-data-jpa
    datasource:
        #你的数据库密码
        password: 123456
        #你的数据库地址
        url: jdbc:mysql://localhost:3306/spring_test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
        #数据库用户名
        username: root
        #数据库驱动名称
        driver-class-name: com.mysql.cj.jdbc.Driver #配置MySQL的驱动程序类
        #指定连接池类型
        type: com.zaxxer.hikari.HikariDataSource
        #数据库连接池的配置
        hikari:
            #客户端等待连接池连接的最大毫秒数
            connection-timeout: 30000
            #连接池中维护的最小空闲连接数
            minimum-idle: 4
            #最大池大小
            maximum-pool-size: 8
            #允许连接池在连接池中空闲的最长时间(毫秒)
            idle-timeout: 30000
            #池中连接关闭的最长生命周期(毫秒)
            max-lifetime: 45000
            #从池返回的连接的默认自动提交行为(默认为true)
            auto-commit: true
            #连接池的名称
            pool-name: SpringDataJPAHikariCP

    #jpa相关配置
    jpa:
        hibernate:
            #DDL:用于定义数据库的三层结构,包括外模式、概念模式、内模式及其相互之间的映像,定义数据的完整性,安全控制等约束
            ddl-auto: none #什么也不做
            #其他可选值
            #create: 每次运行应用程序时,都会重新创建表,所以,数据都会丢失
            #create-drop:每次运行程序时会创建表结构,然后程序结束时清空数据
            #update: 每次运行程序没有表时会创建表,如果对象改变会更新表结构,原有数据不会清除,只会更新
            #validate: 运行程序会校验数据与数据库的字段类型是否相同,字段不同会报错
            #打印执行的sql及参数
        show-sql: true
        # 关闭懒加载配置,否则会报错
        open-in-view: false
        properties:
            hibernate:
                #输出sql语句
                show_sql: true
                #格式化输出的sql,否则会一行显示
                format_sql: true


环境准备:

数据库脚本

这里简单介绍一下表和表之间的关系,一个Banner对应多个BannerItem,属于一对多关系,一个Goods商品可以属于多个商品分类,一个商品分类下面会有多个商品,属于多对多关系,需要用第三张表goods_category_relation来进行组织。

JPA使用:

1.生成Entity实体类
2.创建repository包,编写Repository接口,需要继承JpaRepository接口,指定两个泛型
3.在Repository接口中编写对应方法

3.1 简单查询
3.2 排序
3.3 分页
3.4 复杂查询- 自定义JPQL
3.5 JPA原生查询
3.6 多表联结之一对多关系映射
3.7 多表联结之多对多关系映射
3.8 动态sql

1.生成Entity实体类

@Table(name = “ding_xzbg_admin”)

@Entity:标志为一个JPA的实体类

@Getter、@Setter:表示设置实体类属性的Getter、Setter方法。

@Id注解:标志数据库表的主键字段。

@GeneratedValue(strategy = GenerationType.IDENTITY):指定主键的生成策略,当前指定的策略为主键自增,由数据库进行生成。

@Column:实体和数据表列不同名的时候才有用,如数据库字段banner_id,实体类字段id,这时就要使用@Column注解来标志数据库字段名称@Column(name=“banner_id”)

@Basic:标志是数据库表的列,这个注解会默认加载字段上,如果数据表没有这个字段将会报错。

@Transient:如果想忽略实体类的某一个字段时,可以使用这个注解,JPA就不会将这个字段作为数据表的列。

 @Entity
 @Getter
 @Setter
 public class Banner {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
     private String name;
     private String description;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date modifyTime;
    private Integer deleted;
    private Integer enable;
    private Long creator;
}

2.创建repository包,编写Repository接口,需要继承JpaRepository接口,指定两个泛型

第一个泛型是实体类的类型,第二个泛型是主键的类型。

public interface BannerRepository extends JpaRepository<Banner, Long> {

}

//另一种定义repository的方式
//通过@RepositoryDefinition注解来指定实体类的类型和主键的类型
@RepositoryDefinition(domainClass = Banner.class, idClass = Long.class)
public interface BannerRepositoryDefinition {
}


3.在Repository接口中编写对应方法

3.1 简单查询

通过bannerId 查询banner

 public interface BannerRepository extends JpaRepository<Banner, Long> {
 
     /**
      * 简单查询-- 通过bannerId 查询banner
      *
      * @param bannerId bannerId
      * @return banner 详细信息
      */
     List<Banner> findBannerById(Long bannerId);

}


//测试类
@Test
public void testFindBannerById() {
  //通过bannerId 查询banner
  List<Banner> bannerInfo = bannerRepository.findBannerById(1L);
  log.info(bannerInfo);
}

3.2 排序

在查询方法的入参中加入Sort对象作为入参,就可以实现排序功能

//排序
List<Goods> findAll(Sort sort);

//测试类
@Test
public void testSortFindAll() {	
	//使用Sort.by静态方法来快速构建Sort对象
	//先按照价格降序排列,再按照id升序排列
	Sort sort=Sort.by(Sort.Order.desc("price"), (Sort.Order.asc("id"))  );
	List<Goods> list=goodsRepository.findAll(sort);
  	log.info("{}", JSON.toJSONString( list )  );
}

3.3 分页

在查询方法的入参中加入分页参数Pageable

/**
* JPA实现分页
*/
Page<Goods> findAll(Pageable pageable);

//测试
 @Test
 public void testPageableFindAll() {
 	 //起始页,每页大小,排序对象
     Pageable pageable = PageRequest.of(0, 1,Sort.by(Sort.Order.asc("price")));
     Page<Goods> goodsPage = goodsRepository.findAll(pageable);
     
     log.info("{}", JSON.toJSONString(goodsPage));
     log.info("总页数:{}", goodsPage.getTotalPages());
     log.info("记录总数:{}", goodsPage.getTotalElements());
     log.info("当前页索引(第几页):{}", goodsPage.getNumber());
     log.info("查询的结果信息:{}", goodsPage.getContent());
     log.info("当前页上的元素数量:{}", goodsPage.getNumberOfElements());
     log.info("请求页的Pageable:{}", goodsPage.getPageable());
     log.info("页大小:{}", goodsPage.getSize());
     log.info("页的排序参数:{}", goodsPage.getSort());
     log.info("页面上是否有内容:{}", goodsPage.hasContent());
     log.info("是否有下一页:{}", goodsPage.hasNext());
     log.info("是否有上一页:{}", goodsPage.hasPrevious());
     log.info("当前页是否是最后一个:{}", goodsPage.isLast());
     log.info("下一页的 Pageable:{}", goodsPage.nextPageable());
     log.info("下一页的 Pageable:{}", goodsPage.previousPageable());
}

3.4 复杂查询- 自定义JPQL(无参/有参)

如果JPA规范定义的查询关键字不能满足需求的话,就可以使用@Query自定义查询的JPQL

自定义查询:

/**
  * 查询id值最大的商品信息--无参
  */
@Query(value = "SELECT g from Goods g WHERE id = (SELECT max(id) FROM Goods)", nativeQuery = false)
Goods getMaxIdGoods();



/**
  * 有参--方式一
  */
@Query("SELECT g FROM Goods g WHERE name=?1 and price >= ?2")
List<Goods> findGoodsByFirstParam(String name, BigDecimal price);


/**
  * 有参--方式二
  */
@Query("SELECT g FROM Goods g WHERE name= :name and price >= :price")
List<Goods> findGoodsBySecondParam(@Param("name") String name, @Param("price") BigDecimal price);




只查询某几个字段
如果我们想仅仅查询出某几个字段,可以自定义一个仅包含指定字段的构造函数,如我们只想查询商品名称和价格,那么就可以定义只包含两个字段的构造函数。

public Goods(String name, BigDecimal price) {
        this.name = name;
        this.price = price;
}

编写如下的JPQL查询,使用new的方式在JPQL查询中构造实体类,这是JPQL的特性

@Query("SELECT new Goods (g.name, g.price) FROM Goods g")
List<Goods> getGoodsNameAndPriceInfo();

自定义修改更新:

@Modifying注解来标志是一个更新操作

使用@Transactional注解来标志当前方法不是只读事务

@Query("UPDATE Goods g SET g.price = :price WHERE id = :goodsId")
@Modifying
@Transactional(readOnly = false)
int updateGoodsPrice(@Param("goodsId") Long goodsId,
                        @Param("price") BigDecimal price);

3.5 JPA原生查询

自定义查询都是使用JPQL来进行查询,下面使用原生SQL来进行查询,nativeQuery用来标志当前查询是一条原生SQL
(想要拿到原生查询的部分结果,就使用Map来进行接收)

@Query(value = "SELECT * FROM goods", nativeQuery = true)
List<Goods> findAllNativeQuery();


//如果我们想要拿到原生查询的部分结果,就使用Map来进行接收
@Query(value = "SELECT name, price FROM goods", nativeQuery = true)
List<Map<String, Object>> getGoodsNameAndPriceNativeQuery();


3.6 多表联结之一对多关系映射

数据库表关系:
一个Banner对应多个BannerItem,属于一对多关系
一个Goods商品可以属于多个商品分类,属于一对多关系

在Banner实体类中添加BannerItem的映射关系

@OneToMany注解来指定一对多关系

fetch用来指定当前查询是懒加载还是急加载,这里设置为急加载

@JoinColumn注解指定的是要关联的“多”的那张表的实体类属性,这里要注意是实体类属性而不是数据库字段

@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "bannerId")
private List<BannerItem> bannerItemList;

@Test
public void testOneToManyQuery() {
  List<Banner> allBannerInfo = bannerRepository.findAll();

  log.info("{}", JSON.toJSONString(allBannerInfo));
}

3.7 多表联结之多对多关系映射

数据库表关系:
一个Goods商品可以属于多个商品分类,属于多对多关系
一个商品分类下面会有多个商品,属于多对多关系

@ManyToMany进行多对多配置,使用急加载的方式

@JoinTable注解来指定多对多的第三张表,name属性指定数据表的名称,joinColumn属性指定的是当前实体在第三张表中的关联字段名称

inverseJoinColumns属性指定的是要关联的另一张表的关联字段的名称

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "goods_category_relation", joinColumns = @JoinColumn(name = "category_id"),
inverseJoinColumns = @JoinColumn(name = "goods_id"))
private List<Goods> goodsList;

3.8 动态sql

方法一:用criteria查询
方法二:直接使用原生SQL,构建query来执行

方式一:用criteria查询

//关键代码
Specification<Report> specification=new Specification<Report>() {

            public Predicate toPredicate(Root<Report> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                
                //动态sql拼接
                if (StringUtil.isNotEmpty(vo.getName())) {
                    predicates.add(criteriaBuilder.like(root.get("name"), "%" + vo.getName() + "%"));
                }
                
                criteriaBuilder.and(
                        criteriaBuilder.like(root.get("name"), "%" + vo.getName() + "%")
                );
                return  query.where(predicates.toArray(new Predicate[predicates.size()]));
                
            }
        };

//查询
reportRepository.findAll( specification,pageable);

方法二:直接使用原生SQL,构建query来执行

EntityManager直接创建原生查询
createNativeQuery的参数(string sql,info.class)

//在impl实现类引入
private LocalContainerEntityManagerFactoryBean entityManagerFactory;

//具体的方法
public List<ReportVo>  getFlowReportList(ReportVo vo,Pageable pageable) {
	
	EntityManager em = entityManagerFactory.getNativeEntityManagerFactory().createEntityManager();
	StringBuilder sb=new StringBuilder(300);
	sb.append("SELECT * FROM public.entity where 1=1");

	Map<String,Object> map =  new HashMap<>();
    //拼接动态参数
	   //起草人
	    if( StringUtil.isNotEmpty(vo.getName()) ){
	        sb.append(" and name like :name");
	        map.put("name",vo.getName()+"%");
	    }

	String sql=sb.toString();
	em.getTransaction().begin();
	//创建原生查询的时候,将info.class类即第二个参数,写成要传回的bean,这样就可以直接用List<Bean>接收
	Query query = em.createNativeQuery(sql,ReportVo.class);
	List<ReportVo> obj=query.getResultList();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值