首先了解JPA是什么?
JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种
对象/关联映射工具来管理Java应用中的关系数据。他的出现主要是为了简化现有的持久化开发工作和
整合ORM技术,结束现在Hibernate,TopLink,JDO等ORM框架各自为营的局面。值得注意的是,
JPA是在充分吸收了现有Hibernate,TopLink,JDO等ORM框架的基础上发展而来的,具有易于使用,
伸缩性强等优点。从目前的开发社区的反应上看,JPA受到了极大的支持和赞扬,其中就包括了Spring
与EJB3.0的开发团队。
注意:JPA是一套规范,不是一套产品,那么像Hibernate,TopLink,JDO他们是一套产品,如果
说这些产品实现了这个JPA规范,那么我们就可以叫他们为JPA的实现产品。
spring data jpa
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使
开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩
展!学习并使用 Spring Data JPA 可以极大提高开发效率!
spring data jpa让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现
基本查询
基本查询也分为两种,一种是spring data默认已经实现,一种是根据查询的方法来自动解析成SQL。
预先生成方法
spring data jpa 默认预先生成了一些基本的CURD的方法,例如:增、删、改等等
1 继承JpaRepository
public interface UserRepository extends JpaRepository<User, Long> { }
2 使用默认方法
@Test public void testBaseQuery() throws Exception { User user=new User(); userRepository.findAll(); userRepository.findOne(1l); userRepository.save(user); userRepository.delete(user); userRepository.count(); userRepository.exists(1l); // ... }
自定义简单查询
自定义的简单查询就是根据方法名来自动生成SQL,主要的语法是
findXXBy,readAXXBy,queryXXBy,countXXBy, getXXBy后面跟属性名称:
User findByUserName(String userName);
也使用一些加一些关键字And、 Or
User findByUserNameOrEmail(String username, String email);
修改、删除、统计也是类似语法
Long deleteById(Long id);
Long countByUserName(String userName)
基本上SQL体系中的关键词都可以使用,例如:LIKE、 IgnoreCase、 OrderBy。
List<User> findByEmailLike(String email);
User findByUserNameIgnoreCase(String userName);
List<User> findByUserNameOrderByEmailDesc(String email);
分页查询
分页查询在实际使用中非常普遍了,spring data jpa已经帮我们实现了分页的功能,在查询的
方法中,需要传入参数Pageable
,当查询中有多个参数的时候Pageable
建议做为最后一个参数传入
Page<User> findALL(Pageable pageable);
Page<User> findByUserName(String userName,Pageable pageable);
Pageable
是spring封装的分页实现类,使用的时候需要传入页数、每页条数和排序规则
@Test public void testPageQuery() throws Exception { int page=1,size=10; Sort sort = new Sort(Direction.DESC, "id"); Pageable pageable = new PageRequest(page, size, sort); userRepository.findALL(pageable); userRepository.findByUserName("testName", pageable); }
限制查询
有时候我们只需要查询前N个元素,或者支取前一个实体。
ser findFirstByOrderByLastnameAsc(); User findTopByOrderByAgeDesc(); Page<User> queryFirst10ByLastname(String lastname, Pageable pageable); List<User> findFirst10ByLastname(String lastname, Sort sort); List<User> findTop10ByLastname(String lastname, Pageable pageable);
自定义SQL查询
其实Spring data 觉大部分的SQL都可以根据方法名定义的方式来实现,但是由于某些原因我们想使用
自定义的SQL来查询,spring data也是完美支持的;在SQL的查询方法上面使用@Query
注解,如涉及到
删除和修改在需要加上@Modifying
.也可以根据需要添加 @Transactional
对事物的支持,查询超时的设
置等
@Modifying @Query("update User u set u.userName = ?1 where c.id = ?2") int modifyByIdAndUserId(String userName, Long id); @Transactional @Modifying @Query("delete from User where id = ?1") void deleteByUserId(Long id); @Transactional(timeout = 10) @Query("select u from User u where u.emailAddress = ?1") User findByEmailAddress(String emailAddress);
多表查询
多表查询在spring data jpa中有两种实现方式,第一种是利用hibernate的级联查询来实现,第二种是
创建一个结果集的接口来接收连表查询后的结果,这里主要第二种方式。
首先需要定义一个结果集的接口类。
public interface HotelSummary { City getCity(); String getName(); Double getAverageRating(); default Integer getAverageRatingRounded() { return getAverageRating() == null ? null : (int) Math.round(getAverageRating()); } }
查询的方法返回类型设置为新创建的接口
@Query("select h.city as city, h.name as name, avg(r.rating) as averageRating " - "from Hotel h left outer join h.reviews r where h.city = ?1 group by h") Page<HotelSummary> findByCity(City city, Pageable pageable); @Query("select h.name as name, avg(r.rating) as averageRating " - "from Hotel h left outer join h.reviews r group by h") Page<HotelSummary> findByCity(Pageable pageable);
使用
Page<HotelSummary> hotels = this.hotelRepository.findByCity(new PageRequest(0, 10, Direction.ASC, "name")); for(HotelSummary summay:hotels){ System.out.println("Name" +summay.getName()); }
在运行中Spring会给接口(HotelSummary)自动生产一个代理类来接收返回的结果,代码汇总使用getXX
的形式来获取
多数据源的支持
同源数据库的多源支持
日常项目中因为使用的分布式开发模式,不同的服务有不同的数据源,常常需要在一个项目中使用多个数据源,
因此需要配置sping data jpa对多数据源的使用,一般分一下为三步:
-
1 配置多数据源
-
2 不同源的实体类放入不同包路径
-
3 声明不同的包路径下使用不同的数据源、事务支持
这里有一篇文章写的很清楚:Spring Boot多数据源配置与使用
异构数据库多源支持
比如我们的项目中,即需要对mysql的支持,也需要对mongodb的查询等。
实体类声明@Entity
关系型数据库支持类型、声明@Document
为mongodb支持类型,不同的数据源使用不同的实体就可以了
interface PersonRepository extends Repository<Person, Long> { … } @Entity public class Person { … } interface UserRepository extends Repository<User, Long> { … } @Document public class User { … }
但是,如果User用户既使用mysql也使用mongodb呢,也可以做混合使用
interface JpaPersonRepository extends Repository<Person, Long> { … } interface MongoDBPersonRepository extends Repository<Person, Long> { … } @Entity @Document public class Person { … }
也可以通过对不同的包路径进行声明,比如A包路径下使用mysql,B包路径下使用mongoDB
@EnableJpaRepositories(basePackages = "com.neo.repositories.jpa") @EnableMongoRepositories(basePackages = "com.neo.repositories.mongo") interface Configuration { }
其它
使用枚举
使用枚举的时候,我们希望数据库中存储的是枚举对应的String类型,而不是枚举的索引值,需要在属性上面
添加@Enumerated(EnumType.STRING)
注解
@Enumerated(EnumType.STRING) @Column(nullable = true) private UserType type;
不需要和数据库映射的属性
正常情况下我们在实体类上加入注解@Entity
,就会让实体类和表相关连如果其中某个属性我们不需要和数据库
来关联只是在展示的时候做计算,只需要加上@Transient
属性既可。
@Transient private String userName;
这里有一个开源项目几乎使用了这里介绍的所有标签和布局,大家可以参考:https://github.com/cloudfavorites/favorites-web
代码示例
--------------------------------------------------------------------------------------------------------
1.配置pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>SpringBootJpa</groupId> <artifactId>springBoot-Jpa</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>springBoot-Jpa Maven Webapp</name> <url>http://maven.apache.org</url> <!-- 继承父包 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.1.3.RELEASE</version> <relativePath></relativePath> </parent> <dependencies> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <finalName>springBoot-Jpa</finalName> <!-- 配置java版本 不配置的话默认父类配置的是1.6--> <pluginManagement> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </pluginManagement> </build> </project> 2.创建两个实体类 User.java package com.boot.entity; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "user") public class User implements Serializable{ /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer userId; @Column(name = "id") private Integer id; @Column(name = "user_name") private String userName; @Column(name = "password") private String password; @Column(name = "nick_name") private String nickName; @Column(name="email") private String email; @Column(name="did",insertable=false,updatable=false) private Integer dId; @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH}, fetch=FetchType.EAGER,optional=false) @JoinColumn(name="did",referencedColumnName = "id") private Dept dept; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public User(Integer userId,Integer id, String userName, String password, String nickName, String email, Integer dId) { super(); this.userId = userId; this.id=id; this.userName = userName; this.password = password; this.nickName = nickName; this.email = email; this.dId = dId; } public User() { super(); // TODO Auto-generated constructor stub } public Integer getdId() { return dId; } public void setdId(Integer dId) { this.dId = dId; } public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } } Dept.java package com.boot.entity; import java.io.Serializable; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name = "dept") public class Dept implements Serializable{ /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", nullable = false) private Integer id; @Column(name="dept_id") private Integer deptId; @Column(name="dept_name") private String dept_name; @OneToMany (cascade={CascadeType.REFRESH, CascadeType.MERGE, CascadeType.REMOVE, CascadeType.PERSIST}, fetch=FetchType.LAZY,mappedBy="userId") private Set<User> user; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getDeptId() { return deptId; } public void setDeptId(Integer deptId) { this.deptId = deptId; } public String getDept_name() { return dept_name; } public void setDept_name(String dept_name) { this.dept_name = dept_name; } public Set<User> getUser() { return user; } public void setUser(Set<User> user) { this.user = user; } 3.编写application.properties spring.datasource.platform=mysql spring.datasource.url=jdbc:mysql://localhost/workdatabase?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driverClassName=com.mysql.jdbc.Driver # Advanced configuration... spring.datasource.max-active=50 spring.datasource.max-idle=6 spring.datasource.min-idle=2 spring.datasource.initial-size=6 #create table spring.jpa.hibernate.ddl-auto=validate spring.jpa.database: MYSQL spring.profiles.active=dev server.port=8080 server.session-timeout=30 server.tomcat.uri-encoding=UTF-8 #redirect spring.mvc.view.prefix=/Page/ spring.mvc.view.suffix=.jsp application.message: Hello Phil 5.编写接口IUserService.java package com.boot.serve; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import com.boot.entity.User; public interface IUserServe extends JpaRepository<User, Integer> { /*User findById(Integer id); User findByUserName(String userName); User findByUserNameAndPassword(String userName, String password); List<User> findByUserNameLike(String username); List<User> findByUserNameNotLike(String username); List<User> findByUserNameNot(String username);*/ //使用jpa注解来实现对数据库的操作 // @Query("Select new com.boot.entity.User(u.id,u.userName,u.password,u.nickName,u.email,u.deptId) FROM User u,Dept d WHERE u.deptId = d.deptId and u.deptId = ?1 AND u.password IS NOT NULL") @Query("from User u inner join fetch u.dept as d where d.id=?1") List<User> findByDeptId(Integer deptId); @Query("UPDATE User u SET u.password=?2 WHERE u.userName=?1") @Modifying @Transactional void updatePwd(String userName, String pwd); @Query("DELETE FROM User u WHERE u.userName=?1") @Modifying @Transactional void deleteByUserName(String userName); @Query("UPDATE User u SET u.email= :email WHERE u.userName = :user") @Modifying @Transactional void updateEmail(@Param("user") String userName, @Param("email") String email); } 7.编写测试类 package com.boot.test; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import com.boot.Application; import com.boot.entity.User; import com.boot.serve.IUserServe; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) @WebAppConfiguration public class UserTest { @Autowired private IUserServe userService; @Test public void test1(){ System.out.println("welcome"); } @Test public void testQuery() { List<User> list = userService.findByDeptId(3); /* for(int i =0;i<list.size();i++){ System.out.println(list.get(i).getUserName()+" "+list.get(i).getPassword()+" "+list.get(i).getDeptId()); }*/ for(User u : list){ System.out.println(u.getUserName()+" "+u.getPassword()+" "+u.getNickName()+" "+u.getEmail()+" "); switch(u.getUserId()){ case 1: System.out.println("开发部"); break; case 2: System.out.println("财务部"); break; case 3: System.out.println("测试部"); break; default: System.out.println("商务部"); break; } /*if(u.getdId()==1){ System.out.println("开发部"); }else if(u.getdId()==2){ System.out.println("财务部"); }else if(u.getdId()==3){ System.out.println("测试部"); }else{ System.out.println("商务部"); }*/ } } }