有很多读者留言希望松哥能好好聊聊 Spring Data Jpa!其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring Data 和 Jpa!
JPA 的目标之一是制定一个可以由很多供应商实现的 API,Hibernate 3.2+、TopLink 10.1+ 以及 OpenJPA 都提供了 JPA 的实现,Jpa 供应商有很多,常见的有如下四种:
Spring Data 是 Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。Spring Data 具有如下特点:
为了让大伙彻底把这两个东西学会,这里我就先来介绍单纯的Jpa使用,然后我们再结合 Spring Data 来看 Jpa如何使用。
整体步骤如下:
1.使用 IntelliJ IDEA 创建项目,创建时选择 JavaEE Persistence ,如下:
2.建成功后,添加依赖jar,由于 Jpa 只是一个规范,因此我们说用Jpa实际上必然是用Jpa的某一种实现,那么是哪一种实现呢?当然就是Hibernate了,所以添加的jar,实际上来自 Hibernate,如下:
3.添加实体类
接下来在项目中添加实体类,如下:
@Entity(name = "t_book")
public class Book {
private Long id;
private String name;
private String author;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
// 省略其他getter/setter
}
首先@Entity注解表示这是一个实体类,那么在项目启动时会自动针对该类生成一张表,默认的表名为类名,@Entity注解的name属性表示自定义生成的表名。@Id注解表示这个字段是一个id,@GeneratedValue注解表示主键的自增长策略,对于类中的其他属性,默认都会根据属性名在表中生成相应的字段,字段名和属性名相同,如果开发者想要对字段进行定制,可以使用@Column注解,去配置字段的名称,长度,是否为空等等。
4.创建 persistence.xml 文件
JPA 规范要求在类路径的 META-INF 目录下放置persistence.xml,文件的名称是固定的
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="NewPersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>org.sang.Book</class>
<properties>
<property name="hibernate.connection.url"
value="jdbc:mysql:///jpa01?useUnicode=true&characterEncoding=UTF-8"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="123"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
注意:
5.执行持久化操作
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("NewPersistenceUnit");
EntityManager manager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = manager.getTransaction();
transaction.begin();
Book book = new Book();
book.setAuthor("罗贯中");
book.setName("三国演义");
manager.persist(book);
transaction.commit();
manager.close();
entityManagerFactory.close();
这里首先根据配置文件创建出来一个 EntityManagerFactory ,然后再根据 EntityManagerFactory 的实例创建出来一个 EntityManager ,然后再开启事务,调用 EntityManager 中的 persist 方法执行一次持久化操作,最后提交事务,执行完这些操作后,数据库中旧多出来一个 t_book 表,并且表中有一条数据。
和在 SQL 中一样,JPQL 中的 select 语句用于执行查询。其语法可表示为:
select_clause form_clause[where_clause][groupby_clause][having_clause][orderby_clause]
其中:
在 JPQL 中,查询所有实体的 JPQL 查询语句很简单,如下:
selectofromOrdero或selectofromOrderaso
这里关键字 as 可以省去,标识符变量的命名规范与 Java 标识符相同,且区分大小写,调用 EntityManager 的 createQuery() 方法可创建查询对象,接着调用 Query 接口的 getResultList() 方法就可获得查询结果集,如下:
Query query = entityManager.createQuery( "select o from Order o");
List orders = query.getResultList();
Iterator iterator = orders.iterator();
while(iterator.hasNext() ) {
// 处理Order
}
其他方法的与此类似,这里不再赘述。
在 Spring Boot 中,Spring Data Jpa 官方封装了太多东西了,导致很多人用的时候不知道底层到底是怎么配置的,本文就和大伙来看看在手工的Spring环境下,Spring Data Jpa要怎么配置,配置完成后,用法和 Spring Boot 中的用法是一致的。
首先创建一个普通的Maven工程,并添加如下依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.12.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>5.2.12.Final</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.11.3.RELEASE</version>
</dependency>
</dependencies>
这里除了 Jpa 的依赖之外,就是Spring Data Jpa 的依赖了。
接下来创建一个 User 实体类,创建方式参考 Jpa中实体类的创建方式,这里不再赘述。
接下来在resources目录下创建一个applicationContext.xml文件,并配置Spring和Jpa,如下:
<context:property-placeholder location="classpath:db.properties"/>
<context:component-scan base-package="org.sang"/>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="packagesToScan" value="org.sang.model"/>
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</prop>
</props>
</property>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 配置jpa -->
<jpa:repositories base-package="org.sang.dao"
entity-manager-factory-ref="entityManagerFactory"/>
这里和 Jpa 相关的配置主要是三个,一个是entityManagerFactory,一个是Jpa的事务,还有一个是配置dao的位置,配置完成后,就可以在 org.sang.dao 包下创建相应的 Repository 了,如下:
public interface UserDao extends Repository<User, Long> {
User getUserById(Long id);
}
getUserById表示根据id去查询User对象,只要我们的方法名称符合类似的规范,就不需要写SQL,具体的规范一会来说。好了,接下来,创建 Service 和 Controller 来调用这个方法,如下:
@Service
@Transactional
public class UserService {
@Resource
UserDao userDao;
public User getUserById(Long id) {
return userDao.getUserById(id);
}
}
public void test1() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = ctx.getBean(UserService.class);
User user = userService.getUserById(1L);
System.out.println(user);
}
这样,就可以查询到id为1的用户了。
上文我们自定义的 UserDao 实现了 Repository 接口,这个 Repository 接口是什么来头呢?
首先来看 Repository 的一个继承关系图:
可以看到,实现类不少。那么到底如何理解 Repository 呢?
publicinterfaceRepository<T,IDextendsSerializable>{}
@RepositoryDefinition(domainClass = User.class, idClass = Long.class)
public interface UserDao
{
User findById(Long id);
List<User> findAll();
}
基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能,它的几个常用的实现类如下:
例如:定义一个 Entity 实体类:
class User{
private String firstName;
private String lastName;
}
使用And条件连接时,条件的属性名称与个数要与参数的位置与个数一一对应,如下:
findByLastNameAndFirstName(String lastName,String firstName);
查询举例: 1.按照id查询
User getUserById(Long id);
User getById(Long id);
2.查询所有年龄小于90岁的人
List<User> findByAgeLessThan(Long age);
3.查询所有姓赵的人
List<User> findByUsernameStartingWith(String u);
4.查询所有姓赵的、并且id大于50的人
List<User> findByUsernameStartingWithAndIdGreaterThan(String name, Long id);
5.查询所有姓名中包含"上"字的人
List<User> findByUsernameContaining(String name);
6.查询所有姓赵的或者年龄大于90岁的
List<User> findByUsernameStartingWithOrAgeGreaterThan(String name, Long age);
7.查询所有角色为1的用户
List<User> findByRole_Id(Long id);
支持的查询关键字如下图:
为什么写上方法名,JPA就知道你想干嘛了呢?假如创建如下的查询: findByUserDepUuid()
,框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为Doc:
Page<UserModel> findByName(String name, Pageable pageable);
List<UserModel> findByName(String name, Sort sort);
有的时候,这里提供的查询关键字并不能满足我们的查询需求,这个时候就可以使用 @Query 关键字,来自定义查询 SQL,例如查询Id最大的User:
@Query("select u from t_user u where id=(select max(id) from t_user)")
User getMaxIdUser();
如果查询有参数的话,参数有两种不同的传递方式,
1.利用下标索引传参,索引参数如下所示,索引值从1开始,查询中 ”?X” 个数需要与方法定义的参数个数相一致,并且顺序也要一致:
@Query("select u from t_user u where id>?1 and username like ?2")
List<User> selectUserByParam(Long id, String name);
2.命名参数(推荐):这种方式可以定义好参数名,赋值时采用@Param("参数名"),而不用管顺序:
@Query("select u from t_user u where id>:id and username like :name")
List<User> selectUserByParam2(@Param("name") String name, @Param("id") Long id);
查询时候,也可以是使用原生的SQL查询,如下:
@Query(value = "select * from t_user",nativeQuery = true)
List<User> selectAll();
涉及到数据修改操作,可以使用 @Modifying 注解,@Query 与 @Modifying 这两个 annotation一起声明,可定义个性化更新操作,例如涉及某些字段更新时最为常用,示例如下:
@Modifying
@Query("update t_user set age=:age where id>:id")
int updateUserById(@Param("age") Long age, @Param("id") Long id);
注意:
说到这里,再来顺便说说Spring Data 中的事务问题:
好了,关于Spring Data Jpa 本文就先说这么多,这一块,松哥有一些私藏多年的笔记和视频,如下图: