源碼地址: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');
覺得有用的老鐵贊一下唄~