springboot 和JPA

选择了,web,MySQL,JPA组件作为我们开发必备组件

JPA(Java Persistence API)是Sun官方提出的Java持久化规范,用来方便大家操作数据库。
真正干活的可能是Hibernate,TopLink等等实现了JPA规范的不同厂商,默认是Hibernate。

QueryDslhttps://www.jianshu.com/p/4e9d8adaeeb2

配置数据源以及JPA

配置DataSource以及JPA

用application.yml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test?useUniCode=true&characterEncoding=utf8&useSSL=false
    driverClassName: com.mysql.jdbc.Driver
    username: admin
    password: admin
  jpa:
      database: MySQL
      show-sql: true
       hibernate:
      naming: #命名策略
       physical-strategy:       org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl  #无修改命名
       #physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy   #遇到大写字母 加”_”的命名
       #properties:
       #ddl-auto: update  #自动创建表结构,可以不建表了

ddl-auto:create----每次运行该程序,没有表格会新建表格,表内有数据会清空

ddl-auto:create-drop----每次程序结束的时候会清空表

ddl-auto:update----每次运行程序,没有表格会新建表格,表内有数据不会清空,只会更新

ddl-auto:validate----运行程序会校验数据与数据库的字段类型是否相同,不同会报错
 

 

show-sql用来在控制台输出JPA自语句。

创建实体

我们根据数据库中的字段对应创建一个UserEntity来作为对应操作  实体类注解   主键生成策略

@Transient 不添加在表中

 

映射枚举对象到数据库就需要使用@Enumerated注释进行标注。

@Enumerated(EnumType.STRING)

//@Column(name="address_type")

private AddressType type;//地址类型

package com.cxy.favourite.domain;

import javax.persistence.*;
import java.io.Serializable;

/**
 * 用户
 */
@Entity
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //Mysql为@GenerationType(strategy = GenerationType.IDENTITY)主键
    private Long id;
    @Column(nullable = false,unique = true)
    private String userName;//=nickName
    @Column(nullable = false)
    private String salt;
    @Column(nullable = false)
    private String password;
    @Column(nullable = false,unique = true)
    private String email;
    @Column(nullable = true)
    private String myPicture;//头像
    @Column(length = 65535,nullable = true,columnDefinition = "Text")
    private String introduction;
    @Column(nullable = true)//TODO  之后改成false
    private Long createdTime;
    @Column(nullable = true)//TODO  之后改成false
    private Long lastModifyTime;//最后修改时间
    @Column(nullable = true)
    private String outDate;//作废时间
    @Column(nullable = true)
    private String validataCode;//验证码
    @Column(nullable = true)
    private String backgroundPicture;

    public User() {
        super();
    }

    public User(String userName,String password, String salt, String email) {
        super();
        this.userName = userName;
        this.password = password;
        this.salt = salt;
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getMyPicture() {
        return myPicture;
    }

    public void setMyPicture(String myPicture) {
        this.myPicture = myPicture;
    }

    public String getIntroduction() {
        return introduction;
    }

    public void setIntroduction(String introduction) {
        this.introduction = introduction;
    }

    public Long getCreatedTime() {
        return createdTime;
    }

    public void setCreatedTime(Long createdTime) {
        this.createdTime = createdTime;
    }

    public Long getLastModifyTime() {
        return lastModifyTime;
    }

    public void setLastModifyTime(Long lastModifyTime) {
        this.lastModifyTime = lastModifyTime;
    }

    public String getOutDate() {
        return outDate;
    }

    public void setOutDate(String outDate) {
        this.outDate = outDate;
    }

    public String getValidataCode() {
        return validataCode;
    }

    public void setValidataCode(String validataCode) {
        this.validataCode = validataCode;
    }

    public String getBackgroundPicture() {
        return backgroundPicture;
    }

    public void setBackgroundPicture(String backgroundPicture) {
        this.backgroundPicture = backgroundPicture;
    }
}

创建JPA

实体类已经创建完成了,接着需要使用SpringDataJPA来完成数据库操作,新建名字叫做jpa的package,然后创建UserJPA接口并且继承SpringDataJPA内的接口作为父类,当然还有序列化接口

package com.cxy.jpa;


import com.cxy.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;//简单数据操作
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;//复杂查询

import java.io.Serializable;//序列化接口

public interface UserRepository extends JpaRepository<User,Long>,JpaSpecificationExecutor<User>,Serializable{

}

继承了JpaRepository接口(SpringDataJPA提供的简单数据操作接口)、JpaSpecificationExecutor(SpringDataJPA提供的复杂查询接口)、Serializable(序列化接口)。

其中,JpaRepository接口内有又继承了PagingAndSortingRepository接口以及QueryByExampleExecutor接口,

而PagingAndSortingRepository接口内部又有一个继承自CrudRepository接口

CrudRepository

该接口内包含了最简单的CRUD也就是Create、Read、Update、Delete方法,当然还有count、exists方法

PagingAndSortingRepository

包含了分页,排序等功能

QueryByExampleExecutor

JpaRepository接口继承了该接口,这个接口提供条件查询,复杂查询方法

注意:SpringDataJPA提供的复杂条件查询,需要进行QueryDSL,很复杂, 与Mybatis相比,并不是最好的选择。

不需要做其他的任何操作了,因为SpringBoot以及SpringDataJPA会为我们全部搞定,SpringDataJPA内部使用了类代理的方式让继承了它接口的子接口都以spring管理的Bean的形式存在,也就是说我们可以直接使用@Autowired注解在spring管理bean使用,现在数据库层几乎已经编写完成。

 

控制层编写查询方法

UserController中,然后创建一个查询,添加,更新,删除方法

package com.cxy.favourite.controller;

import com.cxy.favourite.domain.User;
import com.cxy.favourite.jpa.UserRepository;
import org.hibernate.sql.Delete;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 测试 JpaResposity
 */
//@RestController
@RequestMapping("test")
public class TestController {
@Autowired
private UserRepository userRepository;
    /**
     * 查询用户列表方法
     *
     * @return
     */
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public List<User> list() throws Exception{
        return userRepository.findAll();
    }

    /**
     *增加,更新用户方法,添加后返回List,方便测试
     * @Param entity
     * @return
     */
    @RequestMapping(value = "/save",method = RequestMethod.POST)
    public List<User> save(User entity) throws Exception{
        userRepository.save(new User("张三", "123456", "1234r","66666666@qq.com"));
        userRepository.save(new User("小秘书", "123456", "12ggg","12345@qq.com"));
         //userJPA.save方法可以执行添加也可以执行更新,
        // 如果需要执行持久化的实体存在主键值则更新数据,如果为0则添加数据。
        return userRepository.findAll();
    }
    /**
     * 删除用户方法,删除后返回List,方便测试
     *
     * @return
     */
    @RequestMapping(value = "/{id}/delete",method = RequestMethod.GET)
    public List<User> delete(@PathVariable("id")Long id) throws Exception
    {
        User zhang3 = this.userRepository.getOne(id);
       userRepository.delete(zhang3);
        return userRepository.findAll();
    }
}

常见bug

1.启动失败的问题('hibernate.dialect' not set)

Hibernate SQL方言没有设置导致的,在properties文件中增加下面这行:

spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

2.The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one时区错误

application.properties 的adtasource.url加上

&serverTimezone=GMT%2B8 

@Query注解自定义SQL,进行动态SQL

两种方式:

  • 注解
  • 使用EntityManager(点我进入)

@Query是用来配置自定义SQL的注解

nativeQuery = true才是表明了使用原生的sql,如果不配置,默认是false,则使用HQL查询方式。

public interface CategoryDao extends JpaRepository<Category,Integer>,JpaSpecificationExecutor<Category>,Serializable {
       /*查询名字是nb的数据*/
        @Query(value = "select * from category_ where name = ?1",nativeQuery = true)
        public List<Category> nativeQuery (String name);

}
@RequestMapping("/select")
    public List<Category> queryNB(){
    return categoryDao.nativeQuery("nb");
    }

@Query配合@Modifying 完成自定义条件操作

@Modify的clearAutomatically=true的作用

可以清除底层持久化上下文,就是entityManager这个类,我们知道jpa底层实现会有二级缓存,也就是在更新完数据库后,如果后面去用这个对象,你再去查这个对象,这个对象是在一级缓存,但是并没有跟数据库同步,这个时候用clearAutomatically=true,就会刷新hibernate的一级缓存了, 不然你在同一接口中,更新一个对象,接着查询这个对象,那么你查出来的这个对象还是之前的没有更新之前的状态。

从名字上可以看到我们的@Query注解好像只是用来查询的,但是如果配合@Modifying注解一共使用,则可以完成数据的删除、添加、更新操作。现在,根据id 和name 结合删除一条数据

 /*根据id,name删除数据*/
        @Modifying(clearAutomatically=true)
        @Query(value = "delete from category_ where id=?1 and name=?2",nativeQuery = true)
        public void deleteByIdAndName(Integer id,String name);
   @RequestMapping("/modify")
    public List<Category> modify(){
        categoryDao.deleteByIdAndName(5,"nb");
        return categoryDao.findAll();
    }

原因:抛出的异常TranscationRequiredException,意思就是你当前的操作给你抛出了需要事务异常,SpringDataJPA自定义SQL时需要在对应的接口或者调用接口的地方(service)添加事务注解@Transactional,来开启事务自动化管理。 

 /**
     * 设置名字(原生sql)
     * @param introduction
     * @param email
     * @return
     */
    @Modifying(clearAutomatically=true)
    @Transactional(rollbackFor = RuntimeException.class)
    @Query(value = "update user set user_name=?1 where email=?2",nativeQuery=true)
    int setUserName(@Param("userName") String userName, @Param("email") String email);

 /**
     * 设置名字(操作entity)
     * @param introduction
     * @param email
     * @return
     */
    @Modifying(clearAutomatically=true)
    @Transactional(rollbackFor = RuntimeException.class)
    @Query("update User set userName=:userName where email=:email")
   int setUserName(@Param("userName") String userName, @Param("email") String email);

设计思想:

自定义BaseDao

正常情况下不仅仅只继承一个JpaRepository接口,难道业务数据接口每一个都去继承几个相同的接口?答案肯定是 NO,当然多个继承也是可以的,不过对于系统设计还有代码复用性来说并不是最好的选择!

我们创建一个包名叫做base,在包内添加一个BaseRepository接口,并且接口继承我们的JpaRepositor,

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;

import java.io.Serializable;

/**
 * jpa父接口
 * @NoRepositoryBean指明当前这个接口不作为一个Repository创建代理实现类。
 * 用来配置父接口不参与jpa的代理
 */
@NoRepositoryBean
public interface BaseDao<T,ID extends Serializable> extends JpaRepository<T, ID>,JpaSpecificationExecutor<T> {

}

之后创建的接口,直接继承BaseRepository就行了,继承的子接口会拥有JpaRepository,JpaSpecificationExecutor所有方法实现。

查询多个数据如何返回

@Query(value = "select u.userName , u.introduction  ,u.myPicture ,u.id  from Follow f ,User u where f.userId=?1 and f.followId = u.id and f.status = 'FOLLOW'")
    List<Object[]> findFollowUserByUserId(@Param("userId") Long userId);

1.再写个DTO,(利用Collection.stream()  )包装这些返回的数据(还可以考虑使用QueryDsl进行处理)

package com.cxy.favourite.domain.DTO;


import com.cxy.favourite.domain.Entitys;

/**
 * 查询返回对象
 */
public class UserDTO extends Entitys {
    private String userName;
    private String introduction;
    private String myPicture;
    private   Long userId;


    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getIntroduction() {
        return introduction;
    }

    public void setIntroduction(String introduction) {
        this.introduction = introduction;
    }

    public String getMyPicture() {
        return myPicture;
    }

    public void setMyPicture(String myPicture) {
        this.myPicture = myPicture;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

}
    @Test
    public void findFollowedUserByFollowId() throws Exception {

        List<Object[]> result = this.followRepository.findFollowedUserByFollowId(3L);

    List<UserDTO> list =  result.stream().map(tuple ->{
                //创建dto
                UserDTO dto = new UserDTO();
                dto.setUserName(tuple[0].toString());
                dto.setIntroduction(tuple[1].toString());
                dto.setMyPicture(tuple[2].toString());
                dto.setUserId((Long)tuple[3]);
                return dto;
            }).collect(Collectors.toList());
            System.out.println(list);



    }

 

2.

 List<Object> resultList = new ArrayList<>();
        List<Object[]> result = this.followRepository.findFollowUserByUserId(3L);
            for(Object row: result){
                Object[] rowArray = (Object[])row;
                Map<String, Object> mapArr = new HashMap<String, Object>();
                mapArr.put("userName",rowArray[0]);
                mapArr.put("introduction",rowArray[1]);
                mapArr.put("myPicture",rowArray[2]);
                mapArr.put("userId",rowArray[3]);
                resultList.add(mapArr);
            }
            //return resultList

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值