springboot整合springdata-jpa
1.简介
SpringData : Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。
SpringData 项目所支持 NoSQL 存储:
- MongoDB (文档数据库)
- Neo4j(图形数据库)
- Redis(键/值存储)
- Hbase(列族数据库)
SpringData 项目所支持的关系数据存储技术:
- JDBC
- JPA
JPA Spring Data : 致力于减少数据访问层 (DAO) 的开发量, 开发者唯一要做的就只是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!
框架怎么可能代替开发者实现业务逻辑呢?比如:当有一个 UserDao.findUserById() 这样一个方法声明,大致应该能判断出这是根据给定条件的 ID 查询出满足条件的 User 对象。Spring Data JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。
1. Spring Data JPA 进行持久层(即Dao)开发步骤:
声明持久层的接口,该接口继承 Repository(或Repository的子接口,其中定义了一些常用的增删改查,以及分页相关的方法)。
在接口中声明需要的业务方法。Spring Data 将根据给定的策略生成实现代码。
注入service层。
2.JPA、SpringDataJPA、Hibernate关系
JPA是一个规范,Hibernate是一个JPA提供者或实现。Spring Data是Spring Framework的一部分。Spring Data存储库抽象的目标是显著减少为各种持久性存储实现数据访问层所需的代码量。
Spring Data JPA不是JPA提供者。它是一个库/框架,它在我们的JPA提供程序(如Hibernate)的顶部添加了一个额外的抽象层。
Hibernate是一个JPA实现,而Spring Data JPA是一个JPA数据访问抽象。Spring Data提供了GenericDao自定义实现的解决方案,它还可以通过方法名称约定代表您生成JPA查询。
2.SpringBoot整合SpringDataJPA
1.pom.xml加入依赖
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency> <!-- springdata jpa依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
spring-data-jpa会自动引入hobernate相关依赖。
2.application.properties加入下面配置
设置连接属性和hibernate显示语句。
############################################################
#
# datasource settings
#
############################################################
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = 123456
############################################################
#
# SpringDataJPA相关配置
#
############################################################
# Specify the DBMS
#spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.showSql=true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddlAuto=update
其自动配置原理解释:
自动配置的类位于 org.springframework.boot.autoconfigure.orm.jpa 包的JpaProperties类中 :
@ConfigurationProperties(prefix = "spring.jpa")
public class JpaProperties { private Map<String, String> properties = new HashMap<String, String>();
private String databasePlatform;
private Database database;
private boolean generateDdl = false;
private boolean showSql = false; private Hibernate hibernate = new Hibernate();
...
}
3.测试
1. 建立实体:(类似于Hibernate注解实体)
package cn.qlq.springData; import java.util.Date; import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; @Entity
public class User2 {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id; private String username; private String password; private String userfullname; private Date createtime; private String isdeleted; private String sex; private String address; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username == null ? null : username.trim();
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password == null ? null : password.trim();
} public String getUserfullname() {
return userfullname;
} public void setUserfullname(String userfullname) {
this.userfullname = userfullname == null ? null : userfullname.trim();
} public Date getCreatetime() {
return createtime;
} public void setCreatetime(Date createtime) {
this.createtime = createtime;
} public String getIsdeleted() {
return isdeleted;
} public void setIsdeleted(String isdeleted) {
this.isdeleted = isdeleted == null ? null : isdeleted.trim();
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex == null ? null : sex.trim();
} public String getAddress() {
return address;
} public void setAddress(String address) {
this.address = address == null ? null : address.trim();
} @Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", userfullname=" + userfullname
+ ", createtime=" + createtime + ", isdeleted=" + isdeleted + ", sex=" + sex + ", address=" + address
+ "]";
}
}
2.编写dao接口
package cn.qlq.springData;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User2, Integer> {
}
3.编写Controller测试代码,这里省去service层 (基本的增删改查、分页查询)
package cn.qlq.springData; import java.util.ArrayList;
import java.util.List; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
@RequestMapping("user2")
public class UserController2 { @Autowired
private UserDao userDao; @RequestMapping("addUser")
public String adduser() {
for (int i = 0; i < 5; i++) {
User2 user2 = new User2();
user2.setAddress("add" + i);
user2.setUsername("username" + i); userDao.save(user2);
} return "success";
} @RequestMapping("deleteUser")
public String deleteUser() {
// 批量删除
// userDao.deleteAll(); userDao.delete(1);
return "success";
} @RequestMapping("updateUser")
public User2 updateUser() {
User2 user = userDao.getOne(4);
user.setAddress("修改后地址"); User2 user2 = userDao.saveAndFlush(user);
return user2;
} @RequestMapping("getCount")
public Long getCount() {
// 根据条件查询
// userDao.count(example); long count = userDao.count();
return count;
} @RequestMapping("exists")
public boolean exists() {
// 根据条件判断
// userDao.exists(Example<S>); boolean exists = userDao.exists(5);
return exists;
} @RequestMapping("getUser")
public User2 getUser() {
User2 user = userDao.findOne(2); return user;
} @RequestMapping("getUsers")
public List<User2> getUsers() {
// 查询所有
// List<User2> findAll = userDao.findAll(); // 根据ID集合查询
// userDao.findAll(ids); // 根据条件查询:
// Example example = Example.of(user);
User2 user = new User2();
user.setAddress("Add");
user.setUsername("user"); ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains())// 查询username包含修改user
.withIgnorePaths("address");// 忽略address属性 Example<User2> example = Example.of(user, matcher);
List<User2> users = userDao.findAll(example);
return users;
} @RequestMapping("getUsersPage")
public Page<User2> getUsersPage() {
// 根据条件查询:
User2 user = new User2();
user.setAddress("Add");
user.setUsername("user"); ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains())// 查询username包含修改user
.withIgnorePaths("address");// 忽略address属性
// Example example = Example.of(user);
Example<User2> example = Example.of(user, matcher); // 构造排序
List<Sort.Order> orders = new ArrayList<Sort.Order>();
orders.add(new Sort.Order(Sort.Direction.DESC, "id"));
Sort sort = new Sort(orders); // 构造请求参数,页号从0开始。
PageRequest pageRequest = new PageRequest(0, 2, sort); // 如果不带条件不传第一个参数即可
Page<User2> findAll = userDao.findAll(example, pageRequest); return findAll;
} }
上面代码包含了基本的增删改查、判断是否存在、查询总数,以及分页查询,上面所有的批量查询都可以加上排序以及按照条件Example进行查询。
分页封装类Page类已经对分页所需的参数进行了封装,直接使用即可。但是需要注意的是分页参数第一页从0开始。
补充:有时候我们希望公共的实体类抽取一些相同的属性出来,如下:
注意:
1.标注为@MappedSuperclass的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。
2.标注为@MappedSuperclass的类不能再标注@Entity或@Table注解,也无需实现序列化接口。
自增类型ID的抽象实体:
package cn.qs.bean; import java.util.Date; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass; /**
* 所有实体类中相同的部分(自增ID类型的)
*
* @author Administrator
*
*/
@MappedSuperclass
public abstract class AbstractSequenceEntity { @Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected Integer id; protected Date createtime; protected String creator; public AbstractSequenceEntity() {
this.createtime = new Date();
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public Date getCreatetime() {
return createtime;
} public void setCreatetime(Date createtime) {
this.createtime = createtime;
} public String getCreator() {
return creator;
} public void setCreator(String creator) {
this.creator = creator;
} }
UUID类型的抽象实体:
package cn.qs.bean; import java.util.Date; import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass; import org.hibernate.annotations.GenericGenerator; /**
* 所有实体类中相同的部分(UUID类型的)
*
* @author Administrator
*
*/
@MappedSuperclass
public abstract class AbstractUUIDEntity { @Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
protected String id; protected Date createtime; protected String creator; public AbstractUUIDEntity() {
this.createtime = new Date();
} public Date getCreatetime() {
return createtime;
} public void setCreatetime(Date createtime) {
this.createtime = createtime;
} public String getCreator() {
return creator;
} public void setCreator(String creator) {
this.creator = creator;
} }
基类:
package cn.qs.bean.user; import java.util.Date; import javax.persistence.Entity; import cn.qs.bean.AbstractSequenceEntity; @Entity
public class User extends AbstractSequenceEntity { private String username; private String password; private String fullname; private String sex; private String phone; private String email; private Date updatetime; private String roles; private String userblank; public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username == null ? null : username.trim();
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password == null ? null : password.trim();
} public String getFullname() {
return fullname;
} public void setFullname(String fullname) {
this.fullname = fullname == null ? null : fullname.trim();
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex == null ? null : sex.trim();
} public String getPhone() {
return phone;
} public void setPhone(String phone) {
this.phone = phone == null ? null : phone.trim();
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email == null ? null : email.trim();
} public Date getUpdatetime() {
return updatetime;
} public void setUpdatetime(Date updatetime) {
this.updatetime = updatetime;
} public String getRoles() {
return roles;
} public void setRoles(String roles) {
this.roles = roles == null ? null : roles.trim();
} public String getUserblank() {
return userblank;
} public void setUserblank(String userblank) {
this.userblank = userblank == null ? null : userblank.trim();
}
}
补充:JPA执行插入和修改都是Update,JPA对程序调用的save()方法判断是updata或者insert操作的依据是看实体对象的主键是否被赋值。
如果我们传入的bean带ID,JPA会先根据ID查询是否有数据,如果查到数据会执行update语句,如果查不到会执行insert语句;如果不带ID,会执行insert语句。所以比较智能,有对应ID,会先根据ID去查询是否存在数据,如果存在就更新;如果不存在就直接插入。
如下:
public void update(FirstCharge firstCharge) {
firstChargeMapper.saveAndFlush(firstCharge);
}
第一次传的firstCharge存在ID,且数据库中有对应的数据,执行的SQL如下:
Hibernate: select firstcharg0_.id as id1_0_0_, firstcharg0_.amount as amount2_0_0_, firstcharg0_.balance as balance3_0_0_, firstcharg0_.gmt_created as gmt_crea4_0_0_, firstcharg0_.grade as grade5_0_0_, firstcharg0_.parent_name as parent_n6_0_0_, firstcharg0_.register_time as register7_0_0_, firstcharg0_.remark as remark8_0_0_, firstcharg0_.second_parent_name as second_p9_0_0_, firstcharg0_.source_id as source_10_0_0_, firstcharg0_.source_name as source_11_0_0_, firstcharg0_.user_id as user_id12_0_0_, firstcharg0_.user_name as user_na13_0_0_ from first_charge firstcharg0_ where firstcharg0_.id=?
Hibernate: update first_charge set amount=?, balance=?, gmt_created=?, grade=?, parent_name=?, register_time=?, remark=?, second_parent_name=?, source_id=?, source_name=?, user_id=?, user_name=? where id=?
第二次传的firstCharge存在ID,数据库中没有对应的数据,执行的SQL如下:
Hibernate: select firstcharg0_.id as id1_0_0_, firstcharg0_.amount as amount2_0_0_, firstcharg0_.balance as balance3_0_0_, firstcharg0_.gmt_created as gmt_crea4_0_0_, firstcharg0_.grade as grade5_0_0_, firstcharg0_.parent_name as parent_n6_0_0_, firstcharg0_.register_time as register7_0_0_, firstcharg0_.remark as remark8_0_0_, firstcharg0_.second_parent_name as second_p9_0_0_, firstcharg0_.source_id as source_10_0_0_, firstcharg0_.source_name as source_11_0_0_, firstcharg0_.user_id as user_id12_0_0_, firstcharg0_.user_name as user_na13_0_0_ from first_charge firstcharg0_ where firstcharg0_.id=?
Hibernate: insert into first_charge (amount, balance, gmt_created, grade, parent_name, register_time, remark, second_parent_name, source_id, source_name, user_id, user_name) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
JPA执行update的时候会update所有的列,也就是不会更新只有值的列,因此需要我们处理一下:首先查询一遍,然后将修改的列的值赋值到查询到的bean上再去修改
如下:
@Override
public void update(FirstCharge firstCharge) {
FirstCharge systemFirstCharge = firstChargeMapper.findOne(firstCharge.getId());
// 将修改的属性赋值到系统bean上
BeanUtils.copyProperties(systemFirstCharge, firstCharge); firstChargeMapper.saveAndFlush(systemFirstCharge);
}
BeanUtils采用内省将一个bean的属性赋值到另一个bean上:
package cn.qs.utils; import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.apache.commons.lang3.ArrayUtils; public class BeanUtils { /**
* 内省进行数据转换-javaBean转map
*
* @param obj
* 需要转换的bean
* @return 转换完成的map
* @throws Exception
*/
public static <T> Map<String, Object> beanToMap(T obj, boolean putIfNull) throws Exception {
Map<String, Object> map = new HashMap<>();
// 获取javaBean的BeanInfo对象
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass(), Object.class);
// 获取属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获取属性名
String key = propertyDescriptor.getName();
// 获取该属性的值
Method readMethod = propertyDescriptor.getReadMethod();
// 通过反射来调用javaBean定义的getName()方法
Object value = readMethod.invoke(obj); if (value == null && !putIfNull) {
continue;
} map.put(key, value);
} return map;
} public static <T> List<Map<String, Object>> beansToMaps(List<T> objs, boolean putIfNull) throws Exception {
return beansToMaps(objs, putIfNull, false);
} public static <T> List<Map<String, Object>> beansToMaps(List<T> objs, boolean putIfNull, boolean addIndex)
throws Exception { List<Map<String, Object>> result = new ArrayList<>(); Map<String, Object> beanToMap = null;
int index = 0;
for (Object obj : objs) {
beanToMap = beanToMap(obj, putIfNull);
if (addIndex) {
beanToMap.put("index", ++index);
} result.add(beanToMap);
} return result;
} /**
* Map转bean
*
* @param map
* map
* @param clz
* 被转换的类字节码对象
* @return
* @throws Exception
*/
public static <T> T map2Bean(Map<String, Object> map, Class<T> clz) throws Exception {
// new 出一个对象
T obj = clz.newInstance();
// 获取person类的BeanInfo对象
BeanInfo beanInfo = Introspector.getBeanInfo(clz, Object.class);
// 获取属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获取属性名
String key = propertyDescriptor.getName();
Object value = map.get(key); // 通过反射来调用Person的定义的setName()方法
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(obj, value);
}
return obj;
} public static <T> List<T> maps2Beans(List<Map<String, Object>> maps, Class<T> clz) throws Exception {
List<T> result = new ArrayList<>();
for (Map<String, Object> map : maps) {
result.add(map2Bean(map, clz));
} return result;
} /**
* 复制origin的值到dest上
*
* @param dest
* 目标对象
* @param origin
* 元对象
* @param setNull
* 如果源对象属性为null是否覆盖
* @param excludeFieldNames
* 排除的属性
*/
public static <T> void copyProperties(T dest, T origin, boolean setNull, String[] excludeFieldNames) {
try {
// 获取person类的BeanInfo对象
BeanInfo destBeanInfo = Introspector.getBeanInfo(dest.getClass(), Object.class); // 获取目标属性描述器
PropertyDescriptor[] destBeanInfoPropertyDescriptors = destBeanInfo.getPropertyDescriptors(); for (PropertyDescriptor propertyDescriptor : destBeanInfoPropertyDescriptors) {
// 获取属性名
String key = propertyDescriptor.getName();
if (ArrayUtils.contains(excludeFieldNames, key)) {
continue;
} // 获取该属性的值
Method readMethod = propertyDescriptor.getReadMethod(); // 如果源对象没有对应属性就跳过
Object srcValue = null;
try {
srcValue = readMethod.invoke(origin);
} catch (Exception ignored) {
// ignored
continue;
} // 如果源对象的值null且null不设置的时候跳过
if (srcValue == null && !setNull) {
continue;
} // 获取setter方法修改属性
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(dest, srcValue);
}
} catch (Exception ignored) {
// ignored
}
} public static <T> void copyProperties(T dest, T origin) {
copyProperties(dest, origin, false, null);
} public static <T> Object getProperty(T object, String proeprty) {
// 获取javaBean的BeanInfo对象
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(object.getClass(), Object.class);
} catch (IntrospectionException ignore) {
return new Object();
} // 获取属性描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
// 获取属性名
String key = propertyDescriptor.getName();
if (proeprty.equals(key)) {
// 获取该属性的值
Method readMethod = propertyDescriptor.getReadMethod();
// 通过反射来调用javaBean定义的getName()方法
try {
Object value = readMethod.invoke(object);
return value;
} catch (Exception ignore) {
return new Object();
}
}
} return new Object();
}
}
根据上述思路封装的saveOrUpdate方法,如下:
public void saveOrUpdate(FirstCharge firstCharge) {
FirstCharge systemFirstCharge = new FirstCharge();
if (firstCharge.getId() != null) {
systemFirstCharge = firstChargeMapper.findOne(firstCharge.getId());
}
BeanUtils.copyProperties(systemFirstCharge, firstCharge);
firstChargeMapper.saveAndFlush(systemFirstCharge);
}
三、SpringData方法定义规范
1. 简单的条件查询的方法定义规范
方法定义规范如下:
简单条件查询:查询某一个实体或者集合
按照SpringData规范,查询方法于find|read|get开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:属性首字母需要大写。
支持属性的级联查询;若当前类有符合条件的属性, 则优先使用, 而不使用级联属性。 若需要使用级联属性, 则属性之间使用 _ 进行连接。
支持的关键字如下:
| Keyword | Sample | JPQL snippet |
|---|---|---|
And |
findByLastnameAndFirstname |
… where x.lastname = ?1 and x.firstname = ?2 |
Or |
findByLastnameOrFirstname |
… where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals |
findByFirstname,findByFirstnameIs,findByFirstnameEquals |
… where x.firstname = 1? |
Between |
findByStartDateBetween |
… where x.startDate between 1? and ?2 |
LessThan |
findByAgeLessThan |
… where x.age < ?1 |
LessThanEqual |
findByAgeLessThanEqual |
… where x.age <= ?1 |
GreaterThan |
findByAgeGreaterThan |
… where x.age > ?1 |
GreaterThanEqual |
findByAgeGreaterThanEqual |
… where x.age >= ?1 |
After |
findByStartDateAfter |
… where x.startDate > ?1 |
Before |
findByStartDateBefore |
… where x.startDate < ?1 |
IsNull |
findByAgeIsNull |
… where x.age is null |
IsNotNull,NotNull |
findByAge(Is)NotNull |
… where x.age not null |
Like |
findByFirstnameLike |
… where x.firstname like ?1 |
NotLike |
findByFirstnameNotLike |
… where x.firstname not like ?1 |
StartingWith |
findByFirstnameStartingWith |
… where x.firstname like ?1 (parameter bound with appended %) |
EndingWith |
findByFirstnameEndingWith |
… where x.firstname like ?1 (parameter bound with prepended %) |
Containing |
findByFirstnameContaining |
… where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy |
findByAgeOrderByLastnameDesc |
… where x.age = ?1 order by x.lastname desc |
Not |
findByLastnameNot |
… where x.lastname <> ?1 |
In |
findByAgeIn(Collection<Age> ages) |
… where x.age in ?1 |
NotIn |
findByAgeNotIn(Collection<Age> age) |
… where x.age not in ?1 |
True |
findByActiveTrue() |
… where x.active = true |
False |
findByActiveFalse() |
… where x.active = false |
IgnoreCase |
findByFirstnameIgnoreCase |
… where UPPER(x.firstame) = UPPER(?1) |
1. 例如:
接口中写方法根据username和address查询、按username模糊查询总数和数据
package cn.qlq.springData;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User2, Integer> {
// select * from user2 where username=? and address=?
User2 findByUsernameAndAddress(String username, String address);
List<User2> findByUsernameLike(String username);
long countByUsernameLike(String username);
}
测试代码:
@RequestMapping("getUser2ByUsernameAndAddress")
public User2 getUser2ByUsernameAndAddress() {
return userDao.findByUsernameAndAddress("username1", "add1");
}
结果:

生成的SQL如下:
select user2x0_.id as id1_0_, user2x0_.address as address2_0_, user2x0_.createtime as createti3_0_, user2x0_.isdeleted as isdelete4_0_, user2x0_.password as password5_0_, user2x0_.sex as sex6_0_, user2x0_.userfullname as userfull7_0_, user2x0_.username as username8_0_ from user2 user2x0_ where user2x0_.username=? and user2x0_.address=?
2. 例如:级联查询的例子:(根据国家ID查询人)
(1) 增加1个国家实体类
package cn.qlq.springData; import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; @Entity
public class Country {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id; private String countryname; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getCountryname() {
return countryname;
} public void setCountryname(String countryname) {
this.countryname = countryname;
}
}
(2)修改User2.java

(3)启动会自动更新表结构,然后构造下面数据:
country表:

user2

(4)接口增加下面方法:

(5)Controller增加代码:
@RequestMapping("findByCountryId")
public List<User2> findByCountryId() {
return userDao.findByCountryId(1);
}
结果:

3. 查询方法解析流程
(1)方法参数不带特殊参数的查询
假如创建如下的查询:findByUserDepUuid(),框架在解析该方法时,流程如下:
首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为Doc
先判断 userDepUuid(根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续往下走
从右往左截取第一个大写字母开头的字符串(此处为Uuid),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复这一步,继续从右往左截取;最后假设 user 为查询实体的一个属性
接着处理剩下部分(DepUuid),先判断 user 所对应的类型是否有depUuid属性,如果有,则表示该方法最终是根据 "Doc.user.depUuid" 的取值进行查询;否则继续按照步骤3的规则从右往左截取,最终表示根据 "Doc.user.dep.uuid" 的值进行查询。
可能会存在一种特殊情况,比如 Doc包含一个 user 的属性,也有一个 userDep 属性,此时会存在混淆。可以明确在级联的属性之间加上 "_" 以显式表达意图,比如 "findByUser_DepUuid()" 或者 "findByUserDep_uuid()"。
(2) 方法参数带特殊参数的查询
特殊的参数: 还可以直接在方法的参数上加入分页或排序的参数,比如:
Page<User2> findByName(String name, Pageable pageable)
List<User2> findByName(String name, Sort sort);
四、事务以及自定义SQL查询
1.事务
事务控制类似于普通的web开发在service层加入@Transactional注解即可。而且一般事务控制在service层控制。
package cn.qlq.springData; import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
@Transactional
public class User2ServiceImpl implements User2Service {
@Autowired
private UserDao userDao; @Override
public void save(User2 user2) {
userDao.save(user2);
int jj = 1 / 0;
} }
2.自定义SQL查询
有时候我们想复杂的SQL通过自定义sql查询,一般有下面几种方式:
(1)用JPQL查询
专业的术语称为jpql,其用法类似hibernate的hql
索引参数方式获取: 索引值从1开始,查询中'?x'的个数要和方法的参数个数一致,且顺序也要一致
命名参数方式: 可以用':参数名'的形式,在方法参数中使用@Param("参数名")注解,这样就可以不用按顺序来定义形参
自定义的Query查询中jpql语句有like查询时,可以直接把%号写在参数的前后,这样传参数就不用把%号拼接进去了。
例如:
@Query("from User2 where username = ?1 and address = ?2 or address like %?2%")
List<User2> getUserByCondition(String name, String address);
@Query("from User2 where username like %:name%")
List<User2> getUserByUsername(@Param("name") String name);
(2)使用原生的SQL进行查询
编写查询的SQL语句,并且在@Query注解中设置nativeQuery=true。
@Query(value = "select * from user2 where username like %:name%", nativeQuery = true)
List<User2> getUsersByUsername(@Param("name") String username);
唯一的不足是没有找到办法自定义SQL查询可以返回Map和接收Map参数。这个可以采用集成mybatis解决。
五、接口解释
1.Repository 是一个空接口,即是一个标记接口。源码
public interface Repository<T, ID extends Serializable> {
}
2.若我们定义的接口实现了 Repository,则该接口会被 IOC 容器识别为一个 Repository Bean,纳入到 IOC 容器中,进而可以在该接口中定义一些符合规范的方法。最后容器启动的时候会对相应的接口生成代理对象。
3.还可以通过 @RepositoryDefinition 注解来替代继承 Repository 接口。(但是不具备继承接口通用的功能)
4.Repository 接口的实现类:
1)CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
2)PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法(但是不具备条件筛选功能)
3)JpaRepository继承PagingAndSortingRepository接口和QueryByExampleExecutor接口(继承QueryByExampleExecutor接口,可以实现条件查询 )
4)自定义的 XxxxRepository 需要继承 JpaRepository ,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
注: QueryByExampleExecutor: 不属于Repository体系,实现一组 JPA条件查询相关的方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.data.repository.query; import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; public interface QueryByExampleExecutor<T> {
<S extends T> S findOne(Example<S> var1); <S extends T> Iterable<S> findAll(Example<S> var1); <S extends T> Iterable<S> findAll(Example<S> var1, Sort var2); <S extends T> Page<S> findAll(Example<S> var1, Pageable var2); <S extends T> long count(Example<S> var1); <S extends T> boolean exists(Example<S> var1);
}
上面的类图如下:

注意:
SpringDataJPA修改的时候是不会修改有值的列,没有类似于mybatis的updateByPrimaryKeySelective功能,自己在修改数据的时候要特别注意。
补充:如果自己只是想扩展一下JPA接口,不想生成对应的实例,如下:
package cn.qs.mapper; import java.io.Serializable; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean; /**
* 通用的CRUDMapper接口
*
* @author Administrator
*
* @param <T>
* @param <E>
*/
@NoRepositoryBean
public interface BaseCRUDMapper<T, E extends Serializable> extends JpaRepository<T, E> { }
注意:
必须加NoRepositoryBean 注解,否则spring会生成对应的接口实现而报错。
补充:SpringData JPA 在解析实体类字段时驼峰自动添加下划线问题
#列名为驼峰无修改命名
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#遇到大写字母 加_的命名
#spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
补充:基于SpringdataJPA的通用Mapper与Service、Controller的代码如下:
参考:https://github.com/qiao-zhi/SSMTemplate.git
springboot整合springdata-jpa的更多相关文章
- SpringBoot整合SpringData JPA入门到入坟
首先创建一个SpringBoot项目,目录结构如下: 在pom.xml中添加jpa依赖,其它所需依赖自行添加 <dependency> <groupId>org.springf ...
- SpringBoot整合StringData JPA
目录 SpringBoot整合StringData JPA application.yml User.class UserRepository.java UserController SpringBo ...
- 【串线篇】spring boot整合SpringData JPA
一.SpringData简介 其中SpringData JPA底层基于hibernate 二.整合SpringData JPA JPA: Java Persistence API的简称,中文名Java ...
- Springboot集成SpringData JPA
序 StringData JPA 是微服务框架下一款ORM框架,在微服务体系架构下,数据持久化框架,主要为SpringData JPA及Mybatis两种,这两者的具体比较,本文不做阐述,本文只简单阐 ...
- SpringBoot整合SpringData和Mysql数据库
1.新建maven项目(具体的新建过程就不细说了) 2.添加maven依赖,也就是在pom.xml文件添加项目的依赖jar包: <project xmlns="http://maven ...
- 6_5.springboot2.x数据整合springData JPA
1.配置文件 pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</g ...
- 整合SpringData JPA
ORM(Object Relational Mapping): 1).编写一个实体类(bean)和数据表进行映射,并且配置好映射关系: //使用JPA注解配置映射关系 @Entity //告诉JPA这 ...
- SpringBoot整合MongoDB JPA,测试MongoRepository与MongoTemplate用法,简单增删改查+高级聚合
源码 地址 -> https://github.com/TaoPanfeng/case/tree/master/04-mongo/springboot-mongo 一 引入依赖 <depe ...
- 6.4 SpringData JPA的使用
引言:该文档是参考尚硅谷的关于springboot教学视屏后整理而来.当然后面还加入了一些自己从网上收集整理而来的案例! 一.SpringData JPA初步使用 1. springdata简介 2. ...
- 尚硅谷springboot学习34-整合SpringData JPA
SpringData简介
随机推荐
- 关于使用tradingview插件的一些心得
1.禁用自带的一些功能 disabled_features: [ // 开启图表功能的字符串文字 允许将用户设置保存到本地存储 'header_symbol_search', // 头部搜索 &quo ...
- 基于Metronic的Bootstrap开发框架--资产编码打印处理
在开发业务管理系统的时候,往往涉及到资产信息及编码的打印处理,如我们需要对资产信息.条形码.二维码一起打印,以便贴在具体资产信息上面,方便微信公众号.企业微信进行业务处理,那么编码的打印就很有必要了, ...
- Spring Boot与分布式
---恢复内容开始--- 分布式.Dubbo/Zookeeper.Spring Boot/Cloud 一.分布式应用 在分布式系统中,国内常用zookeeper+dubbo组合, 而Spring Bo ...
- 网络视频会议openmeetings Windows安装
官网 http://openmeetings.apache.org/index.html 下载文件解压运行install-service脚本之后运行red5脚本启动 官方安装文档 http://ope ...
- Golang 介绍与安装
1.介绍与安装 Golang 是什么 Go 亦称为 Golang(按照 Rob Pike 说法,语言叫做 Go,Golang 只是官方网站的网址),是由谷歌开发的一个开源的编译型的静态语言. Gola ...
- python学习日记(面向对象——继承)
什么是继承 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类 python中类的继承分为:单继承和多继承 class Par ...
- 如何用Electron Js创建第一个应用Hello World
什么是Electron Node.js和Chromium的结合品.允许只使用HTML,CSS和JavaScript来开发跨平台桌面应用. 编写第一个Electron程序(Hello World) 在开 ...
- did not finish being created even after we waited 189 seconds or 61 attempts. And its status is downloading
did not finish being created even after we waited 189 seconds or 61 attempts. And its status is down ...
- Django视图
Django的View(视图) 一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一个404错误, ...
- EOF输入
EOF是一个计算机术语,为End Of File的缩写,在操作系统中表示资料源无更多的资料可读取.资料源通常称为档案或串流.通常在文本的最后存在此字符表示资料结束.是int类型的宏定义,它扩展为负整数 ...