jpa的使用(spring-data-jpa)

源碼地址:https://github.com/bigBigRiver/redis.git

相比於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');

覺得有用的老鐵贊一下唄~

發佈了86 篇原創文章 · 獲贊 144 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章