jpa的使用(spring-data-jpa)

相比于Mybatis,jpa的使用方便简洁,功能也很强大,也是springboot的集成模块里的!当然,Mybatis也有它的优点,比如灵活等,各有各的好!一个是看个人的喜欢,一个是看公司使用的技术栈,多掌握一门技术总是好的!

JPA、hibernate和spring-data-jpa的关系。

JPA(Java Persistence API)是将实体对象持久化到数据库中的一种规范。JPA仅仅是一种规范,也就是说JPA仅仅定义了一些接口,而接口是需要实现才能工作的。所以底层需要某种实现,而Hibernate就是实现了JPA接口的ORM框架spirng data jpa是spring提供的一套简化JPA开发的框架,按照约定好的【方法命名规则】写dao层接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作。同时提供了很多除了CRUD之外的功能,如分页、排序、复杂查询等等,底层还是使用了 Hibernate 的 JPA 技术实现。

一、添加依赖

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

二、配置application.yml文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: yourUserName
    password: yourPassword
    url: jdbc:mysql://localhost:3306/jpa_test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
  jpa:
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5Dialect
#        dialect: org.hibernate.dialect.MySQLDialect
        format_sql: true
    hibernate:
      ddl-auto: update
#      ddl-auto: create

说明:

1、使用第一个注释(dialect: org.hibernate.dialect.MySQLDialect),可能会在由实体类生成数据库表的时候报错(Error executing DDL),DDL是数据定义语言(Data Definition Language),用来创建数据库表和更新数据库表结构的。

2、使用第二个注释(ddl-auto: create),会把数据库中的所有表和数据都删除,然后再重新创建新表,所以要慎用!使用update就安全,也会创建表,更新表结构!为了安全起见,尽量避免使用ddl-auto: create。

3、serverTimezone=GMT%2B8,配置的是东八区。

4、format_sql: true,格式化显示sql,这个也很重要,方便查看sql的执行情况!

三、继承JpaRepository接口

/**
 * @author river
 * 2020/1/21
 */
public interface UserRepository extends JpaRepository<User,Integer> {

    User findByUserName(String userName);

    List<User> findByUserNameEndsWith(String userName);

    List<User> findByUserNameStartsWith(String userName);

    List<User> findByUserNameContains(String userName);

    List<User> findByUserNameLike(String userName);

    List<User> findByUserNameNotIn(String[] userNames);

    @Override
    Page<User> findAll(Pageable pageable);

    /*原生查询,列名和表名使用数据库中的字段和表名*/
    @Query(value = "select * from user where user_name like ?1",
            countQuery = "select count(*) from user where user_name like ?1",nativeQuery = true)
    Page<User> findByUserNameLike(String userName, Pageable pageable);

    /*hql语句,列名和表名使用实体类的属性和类名,如果没有new User(),返回值List中是Object[],而不是User实体类*/
    @Query(value = "select new User(u.userName, u.password) from User u where u.userName like :userName and u.createTime > :createTime")
    List<User> findByUserNameLikeAndCreateTimeGreaterThan(@Param("userName")String userName, @Param("createTime") Date createTime);
}

说明:

1、JpaRepository<User,Integer>中,User是数据库对象的实体类,Integer是数据表的主键类型。

2、UserName为字段名,查询的时候,只要函数的命名符合jpa的规则,则不需要实现的方法体,我列举的几个常用的查询。

3、jpa实现分页的方式是使用Pageable接口。

4、可以使用原生查询hql语句查询。其中hql中,需要注意的是new User(),描述在注释中写了。

四、编写测试类UserRepositoryTest

/**
 * @author river
 * 2020/1/21
 */
@Slf4j
@SpringBootTest
class UserRepositoryTest {

    @Autowired
    UserRepository userRepository;

    @Test
    void save() {
        User user = new User("river","123456");
        User user1 = userRepository.save(user);
        log.info("id:{}, userName:{}, password:{}",user1.getId(),user1.getUserName(),user1.getPassword());
    }

    @Test
    void findByUserName() {
        User user = userRepository.findByUserName("river");
        assertNotEquals(user,null);
        log.info(user.getPassword());
    }

    @Test
    void update(){
        User user = userRepository.findByUserName("river");
        user.setPassword("123123");
        userRepository.save(user);
        assertNotEquals(user,null);
        log.info(user.getPassword());
    }

    @Test
    void findByUserNameEndsWith() {
        List<User> userList = userRepository.findByUserNameEndsWith("ver3");
        assertEquals(userList.size(),1);
        log.info(userList.get(0).getPassword());
    }

    @Test
    void findByUserNameStartsWith() {
        List<User> userList = userRepository.findByUserNameStartsWith("river");
        assertNotEquals(userList.size(), 0);
    }

    @Test
    void findByUserNameContains() {
        List<User> userList = userRepository.findByUserNameContains("3");
        assertEquals(userList.size(),1);
        log.info(userList.get(0).getPassword());
    }

    @Test
    void findByUserNameLike() {
        List<User> userList = userRepository.findByUserNameLike("river%");
        assertNotEquals(userList.size(), 0);
        log.info("size:{}",userList.size());
        userList = userRepository.findByUserNameLike("river_");
        assertNotEquals(userList.size(), 0);
        log.info("size:{}",userList.size());
    }

    @Test
    void findByUserNameNotIn() {
        String[] userNames = {"river", "river1", "river4"};
        List<User> userList = userRepository.findByUserNameNotIn(userNames);
        assertNotEquals(userList.size(), 0);
        log.info("size:{}",userList.size());
    }

    @Test
    void findAll() {
        Sort sort = Sort.by(Sort.Direction.DESC,"createTime");//创建时间降序排序
        PageRequest pageRequest = PageRequest.of(0,3,sort);//查询第一页,一页3条记录

        Page<User> page = userRepository.findAll(pageRequest);
        List<User> userList = page.getContent();
        assertEquals(userList.size(), 3);
    }

    @Test
    void findByUserName1() {
        PageRequest pageRequest = PageRequest.of(0, 3);//查询第一页,一页3条记录
        Page<User> page = userRepository.findByUserNameLike("river%",pageRequest);
        List<User> userList = page.getContent();
        assertEquals(userList.size(), 3);
    }


    @Test
    void findByUserNameLikeAndCreateTimeGreaterThan() throws ParseException {
        String dateString = "2020-01-13 21:53:57";
        String strDateFormat = "yyyy-MM-dd HH:mm:ss";
        DateFormat df = new SimpleDateFormat(strDateFormat);
        Date date = df.parse(dateString);
        List<User> userList = userRepository.findByUserNameLikeAndCreateTimeGreaterThan("river%", date);
        assertNotEquals(userList.size(), 0);
        for (User user : userList) {
            log.info(user.getUserName());
        }
    }
}

实体类User:

@Entity
@Data
@DynamicInsert
@DynamicUpdate
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)/*配置之后返回的id不为0*/
    private int id;

    @Column(name="user_name")
    private String userName;

    @Column(name="password")
    private String password;

    @Column(name="create_time")
    private Date createTime;

    @Column(name="update_time")
    private Date updateTime;

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

    }
}

说明:

1、save()方法,返回的User对象中,id是为0的。除非配置了@GeneratedValue

2、createTime是不需要赋值的。配置数据库的时候,设置默认值为:CURRENT_TIMESTAMP。还需要在User实体类加上@DynamicInsert注解,这样插入的时候createTime就会被自动赋值。

3、updateTime也是不需要赋值的。设计数据表的使用,手动建表的话:`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。如果是使用Navicat设计表的话,勾选“根据当前时间戳更新”。还需要在在User实体类加上@DynamicUpdate注解,这样当数据更新的时候,时间会自动更新到数据库中。

4、Mysql5.6版本之前的,是不可以使用两个默认值为CURRENT_TIMESTAMP低版本的需要更新,可以参考我的博客:https://blog.csdn.net/river66/article/details/104046097

5、关于@Data@Slf4j的使用,可以参考:

https://blog.csdn.net/river66/article/details/103727367

https://blog.csdn.net/river66/article/details/103713397

除了以上的内容,JPA还有一个技术点是根据数据库表生成实体类。

1、Ctrl+Alt+Shift+S:打开Project Structure窗口,在Module中添加JPA模块。

2、选中:View-->Tool Windows-->Persistence,打开Persistence窗口。

3、选中项目,右键:Generate Persistence Mapping-->By Database Schema,然后选择数据源和生成的包路径就可以啦!

最后sql如下:

/*
Navicat MySQL Data Transfer

Source Server         : superMeSql
Source Server Version : 80019
Source Host           : localhost:3306
Source Database       : jpa_test

Target Server Type    : MYSQL
Target Server Version : 80019
File Encoding         : 65001

Date: 2020-01-21 12:39:41
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'river', '123123', '2020-01-21 12:33:20', '2020-01-21 12:37:19');
INSERT INTO `user` VALUES ('2', 'river1', '123456', '2020-01-21 12:35:49', '2020-01-21 12:35:49');
INSERT INTO `user` VALUES ('3', 'river2', '123456', '2020-01-21 12:36:22', '2020-01-21 12:36:22');
INSERT INTO `user` VALUES ('4', 'river3', '123456', '2020-01-21 12:36:35', '2020-01-21 12:36:35');
INSERT INTO `user` VALUES ('5', 'river4', '123456', '2020-01-21 12:36:48', '2020-01-21 12:36:48');

觉得有用的老铁赞一下呗~

发布了79 篇原创文章 · 获赞 132 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/river66/article/details/104058806