基本概念、hibernate、SpringDataJpa
基本概念
1
JPA
JPA (Java Persistence API)Java持久化API。是一套Sun公司Java官方制定的ORM 方案,是规范,是标准
2
ORM
ORM(Object Relational Mapping)对象关系映射。在操作数据库之前,先把数据表与实体类关联起来。然后通过实体类的对象操作(增删改查)数据库表
3
hibernate
对象关系映射框架,对JDBC进行轻量级的封装,是一个全自动ORM框架,可自动生成SQL语句,自动执行
hibernate
1
依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- hibernate对jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- log日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
2
配置文件
建立一个文件夹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:持久化单元名称
transaction-type:事务管理的方式
JTA:分布式事务管理
RESOURCE_LOCAL:本地事务管理
-->
<persistence-unit name="mysqljpa" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!--配置数据库信息-->
<property name="javax.persistence.jdbc.user" value="root"></property>
<property name="javax.persistence.jdbc.password" value=""></property>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"></property>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///test2"></property>
<!--配置jpa实现方(hibernate)的配置信息
显示sql : false|true
自动创建数据库表 : hibernate.hbm2ddl.auto
create : 程序运行时创建数据库表(如果有表,先删除表再创建)
update :程序运行时创建表(如果有表,不会创建表)
none :不会创建表
-->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
</persistence>
log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
3
实体类
package entity;
import javax.persistence.*;
@Entity//表示这个类是要建立映射关系的实体类
@Table(name="account")//这个实体类映射的表格
public class Account {
@Id//主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//自增
private int id;
@Column(name = "name")//映射到表的列
private String name;
@Column(name = "money")
private int money;
public Account(String name, int money) {
this.name = name;
this.money = money;
}
public Account() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
@GeneratedValue的strategy
strategy | 说明 |
---|---|
GenerationType.IDENTITY | 数据库机制,底层数据库支持自动增长,mysql |
GenerationType.SEQUENCE | 数据库机制,底层数据库支持支持序列,oracle |
GenerationType.TABLE | JPA机制,通过一张数据库表完成自增 |
GenerationType.AUTO | 自动 |
4
增删改查
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import entity.Account;
public class Test {
static EntityManagerFactory factory;
static EntityManager entityManager;
static EntityTransaction transaction;
static {
factory= Persistence.createEntityManagerFactory ("mysqljpa");
entityManager=factory.createEntityManager ();
transaction=entityManager.getTransaction ();
}
@org.junit.Test
public void add(){//新增
transaction.begin ();
Account account=new Account ();
account.setName ("abc");
account.setMoney (1000);
entityManager.persist (account);
transaction.commit ();
System.out.println ("-------------------华丽的分割线-----------------------");
System.out.println (account);//id是插入数据库后返回的id值
entityManager.close ();
}
@org.junit.Test
public void delete(){//删除
transaction.begin ();
Account account=entityManager.find (Account.class,11);
entityManager.remove (account);
transaction.commit ();
entityManager.close ();
}
@org.junit.Test
public void update(){//更新
transaction.begin ();
Account account=entityManager.find (Account.class,8);
account.setMoney (2000);
entityManager.merge (account);
transaction.commit ();
entityManager.close ();
}
@org.junit.Test
public void find(){
Account account=entityManager.find (Account.class,1);
Account account1=entityManager.getReference (Account.class,3);//懒加载方式查询
System.out.println (account);
System.out.println (account1);//不执行这一句,将不会执行sql
entityManager.close ();
}
}
find和getReference区别
(1)前者返回的是传入类的对象,后者返回的是传入类的代理对象
(2)前者立即执行sql,后者在使用返回对象时执行sql
5
JPQL
一种操作实体类的的语句,类似SQL。jpql与SQL的区别就是SQL是面向对象关系数据库,他操作的是数据表和数据列,而jpql操作的对象是实体对象和实体属性
封装一个获取EntityManager的工具
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class jpaUtils {
static EntityManagerFactory factory;
static EntityManager entityManager;
static {
factory= Persistence.createEntityManagerFactory ("mysqljpa");
entityManager=factory.createEntityManager ();
}
public static EntityManager getEntityManager(){
return entityManager;
}
}
详细
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.List;
public class jpql {
@Test
public void findall(){//查询全部
EntityManager entityManager=jpaUtils.getEntityManager ();
String jpql="from entity.Account";//相当于SELECT * FROM account
Query query=entityManager.createQuery (jpql);
List list=query.getResultList ();
System.out.println (list);
}
@Test
public void findallDest(){//排序查询
EntityManager entityManager=jpaUtils.getEntityManager ();
String jpql="from entity.Account order by id desc ";//相当于SELECT * FROM account ORDER BY id DESC
Query query=entityManager.createQuery (jpql);
List list=query.getResultList ();
System.out.println (list);
}
@Test
public void findallCount(){//统计查询
EntityManager entityManager=jpaUtils.getEntityManager ();
String jpql="select count(id) from entity.Account";//相当于SELECT COUNT(id) FROM account
Query query=entityManager.createQuery (jpql);
List list=query.getResultList ();
System.out.println (list);
}
@Test
public void findallLimit(){//分页查询
EntityManager entityManager=jpaUtils.getEntityManager ();
String jpql="from entity.Account";
Query query=entityManager.createQuery (jpql);
query.setFirstResult (0);
query.setMaxResults (5);
//相当于SELECT * FROM account LIMIT 0,5
List list=query.getResultList ();
System.out.println (list);
}
@Test
public void findallWhere(){//条件查询
EntityManager entityManager=jpaUtils.getEntityManager ();
String jpql="from entity.Account where name like ?";
Query query=entityManager.createQuery (jpql);
query.setParameter (1,"%S%");
//相当于SELECT * FROM account WHERE name like '%S%'
List list=query.getResultList ();
System.out.println (list);
}
}
SpringDataJpa
是 spring data 项目下的一个模块。提供了一套基于 JPA标准操作数据库的简化方案。底层默认的是依赖 Hibernate JPA 来实现的
1
依赖
<properties>
<spring.version>5.0.2.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>5.1.6</mysql.version>
</properties>
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring beg -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring对orm框架的支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end -->
<!-- hibernate beg -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!-- hibernate end -->
<!-- c3p0 beg -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- c3p0 end -->
<!-- log end -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- spring data jpa 的坐标-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- el beg 使用spring data jpa 必须引入 -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<!-- el end -->
</dependencies>
2
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--spring 和 spring data jpa的配置-->
<!-- 1.创建entityManagerFactory对象交给spring容器管理-->
<bean id="entityManagerFactoty" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--配置的扫描的包(实体类所在的包) -->
<property name="packagesToScan" value="entity" />
<!-- jpa的实现厂家 -->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!--jpa的供应商适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--配置是否自动创建数据库表 -->
<property name="generateDdl" value="false" />
<!--指定数据库类型 -->
<property name="database" value="MYSQL" />
<!--数据库方言:支持的特有语法 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<!--是否显示sql -->
<property name="showSql" value="true" />
</bean>
</property>
<!--jpa的方言 :高级的特性 -->
<property name="jpaDialect" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
<!--jpa实现方式的配置信息-->
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!--2.创建数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value=""></property>
<property name="jdbcUrl" value="jdbc:mysql:///test2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8" ></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--3.整合spring dataJpa-->
<jpa:repositories base-package="dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactoty" ></jpa:repositories>
<!--4.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoty"></property>
</bean>
<!-- 4.txAdvice-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 5.aop-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
<!--5.声明式事务 -->
<!-- 6. 配置包扫描-->
<context:component-scan base-package="java" ></context:component-scan>
</beans>
3
实体类
package entity;
import javax.persistence.*;
@Entity
@Table(name = "account")
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "money")
private int money;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMooney() {
return money;
}
public void setMooney(int mooney) {
this.money = mooney;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", mooney=" + money +
'}';
}
}
1)jpa通过实体类创建的表引擎时MYSIAM,如果两张相关联的表引擎不同,在项目启动时会报错
2)如果出现org.springframework.orm.hibernate3.HibernateSystemException: Null value was assigned to a property
这个错误,在实体类中将报错字段的类型设置为其包装类可解决
4
dao
package dao;
import entity.Account;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface AccountDao extends JpaRepository<Account,Long>, JpaSpecificationExecutor<Account> {//继承2个接口,将实体类传入
}
5
普通增删改查
import dao.AccountDao;
import entity.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:ApplicationContex.xml")
public class test {
@Autowired
private AccountDao accountDao;
@Test
public void findAll(){//查询所有
List list=accountDao.findAll ();
System.out.println (list);
}
@Test
public void findOne(){//查询一条记录
Account account=accountDao.findOne (5L);
System.out.println (account);
}
@Test
public void delete(){//删除一条记录
accountDao.delete (6L);
}
@Test
public void update(){//修改一条记录
Account account=accountDao.findOne (5L);
account.setName ("sylvester");
accountDao.save (account);
}
@Test
public void insert(){//插入一条记录
Account account=new Account ();
account.setName ("abcde");
account.setMooney (1000);
accountDao.save (account);
}
@Test
public void count(){//查询总数
long count=accountDao.count ();
System.out.println (count);
}
@Test
public void exist(){//查询某条记录是否存在
boolean exists=accountDao.exists (5L);
System.out.println (exists);
}
@Test
@Transactional
public void getOne(){//查询一条记录,延迟加载,必须加上@Transactional
Account account=accountDao.getOne (5L);
System.out.println (account);
}
}
6
JPQL查询
1
步骤
1)dao接口中定义方法
2)在方法上加@Query注解,并将jpql语句写在里面
2
详细
1)AccountDao
package dao;
import entity.Account;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface AccountDao extends JpaRepository<Account,Long>, JpaSpecificationExecutor<Account> {
@Query(value = "from entity.Account where name=?1")
List<Account> findByName(String name);
@Query(value = "from entity.Account where name=?1 and id=?2")
Account findByNameAndId(String name,Long id);
@Query(value = "update entity.Account set name=?1 where id=?2")
@Modifying//表明改操作是更新操作
void updateAccount(String name,long id);
//原生sql
@Query(nativeQuery =true,value = "select * from account")
List<Object[]> findAllAcount();
}
注意
如果时更新方法,要在方法上加@Modifying
2)测试代码
import dao.AccountDao;
import entity.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:ApplicationContex.xml")
public class test1 {
@Autowired
AccountDao accountDao;
@Test
public void findbyname(){//按照姓名查找
List<Account> accounts=accountDao.findByName ("abc");
System.out.println (accounts);
}
@Test
public void findbynameandid(){//按照姓名和id查找
Account account=accountDao.findByNameAndId ("sylvester",5L);
System.out.println (account);
}
@Test
@Transactional//开启事务
@Rollback(false)//关闭回滚
public void updateaccount(){//更新
accountDao.updateAccount ("sylvester1",5);
}
//更新操作一定要加上事务,否则报错
@Test
public void nativefindall(){//原生sql查找
List<Object[]> list=accountDao.findAllAcount ();
for (Object[] row:
list) {
System.out.println (Arrays.toString (row ));
}
}
}
7
方法命名规则查询
spring对JPQL更深一层的封装,只需按方法规则定义方法,无需配JPQL语句
1)AccountDao
package dao;
import entity.Account;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
public interface AccountDao extends JpaRepository<Account,Long>, JpaSpecificationExecutor<Account> {
List<Account> findByName(String name);
List<Account> findByNameLike(String name);
List<Account> findByNameLikeAndMoney(String name,int money);
}
2)测试代码
import dao.AccountDao;
import entity.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:ApplicationContex.xml")
public class test2 {
@Autowired
AccountDao accountDao;
@Test
public void test_findbyname(){//按姓名查找
List<Account> accounts=accountDao.findByName("sylvester1");
System.out.println (accounts);
}
@Test
public void test_findbynamelike(){//模糊查询
List<Account> accounts=accountDao.findByNameLike("%s%");
System.out.println (accounts);
}
@Test
public void test_findbynamelikeandmoney(){//多条件查询
List<Account> accounts=accountDao.findByNameLikeAndMoney("%s%",1000);
System.out.println (accounts);
}
}
3)查找外键字段
package com.example.sub_for_dep.responsitory;
import com.example.sub_for_dep.entity.Subject;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface SubjectResponsitory extends JpaRepository<Subject,Long> {
@Query("from Subject as s where s.subject_type_id=:subject_type_id")
Page<Subject> findAllBySubject_type_id(@Param ("subject_type_id") long subject_type_id, Pageable pg);
}
subject_type_id是一个外键的字段,只能使用jpql查询,否则报错
8
specifications动态查询
创建一个Specification对象,在对象里定义查询规则,再将该对象传入查询方法中
import dao.AccountDao;
import entity.Account;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.persistence.criteria.*;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:ApplicationContex.xml")
public class test3 {
@Autowired
AccountDao accountDao;
@Test
public void findbyname(){//按名字查询
Specification<Account> spc=new Specification<Account> ( ) {
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> name=root.get("name");//选择列
Predicate predicate=criteriaBuilder.equal (name,"sylvester");//设置查询条件name=sylvester
return predicate;
}
};
List<Account> accounts=accountDao.findAll (spc);
System.out.println (accounts);
}
@Test
public void findbynameandmoney(){//多条件查询
Specification<Account> spc=new Specification<Account> ( ) {
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> name=root.get("name");
Path<Object> money=root.get("money");
Predicate p1=criteriaBuilder.equal (name,"sylvester");
Predicate p2=criteriaBuilder.equal (money,1000);
Predicate and=criteriaBuilder.and (p1,p2);
return and;
}
};
List<Account> accounts=accountDao.findAll (spc);
System.out.println (accounts);
}
@Test
public void findbynamelike(){//模糊查询
Specification<Account> spc=new Specification<Account> ( ) {
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> name=root.get("name");
Path<Object> money=root.get("money");
Predicate predicate=criteriaBuilder.like (name.as(String.class),"%s%");
//注意gt,lt,ge,le,like需指定比较参数类型
return predicate;
}
};
List<Account> accounts=accountDao.findAll (spc);
System.out.println (accounts);
}
@Test
public void findallsorted(){//倒序查询
Sort sort=new Sort (Sort.Direction.DESC,"id");//按id倒序排列
List<Account> accounts=accountDao.findAll (sort);
System.out.println (accounts);
}
@Test
public void findallpage(){//分页查询
Pageable pageable=new PageRequest (1,5);//第一个参数是当前页数,第二个参数是每页数据数量
Page<Account> page=accountDao.findAll (pageable);
System.out.println (page.getContent ());//获取当前页的所有数据
System.out.println (page.getTotalElements ());//获取当前有多少数据
System.out.println (page.getTotalPages ());//获取当前有多少页
}
@Test
public void countall(){//查询在某个条件下有多少数据
Specification<Account> spc=new Specification<Account> ( ) {
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Path<Object> name=root.get("name");
Predicate predicate=criteriaBuilder.equal (name,"sylvester");
return predicate;
}
};
long num=accountDao.count(spc);
System.out.println (num);
}
}
9
一对多表
对于表之间一对多的关系,只需要在实体类中配置即可
如:书和出版社的关系:1本书对应1个出版社,1个出版社对应多本书
1)实体类Book
package entity;
import javax.persistence.*;
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "book_id")
private long book_id;
@Column(name = "name")
private String name;
@Column(name ="publisher_id")
private long publisher_id;
@ManyToOne(targetEntity = Publisher.class,fetch = FetchType.LAZY)//targetEntity指向相关联的另一张表
@JoinColumn(name = "book_pub_id",referencedColumnName = "publisher_id",insertable = false,updatable = false)//name为book表的外键名,referencedColumnName指向publisher表的主键;两个实体映射到同一张表或者有两个属性(一个是一般属性,一个是多对一的属性)映射到数据库的同一列,就会报错,需要关闭掉该字段的插入、更新操作,如insertable = false, updatable = false
private Publisher publisher;
public long getBook_id() {
return book_id;
}
public void setBook_id(long book_id) {
this.book_id = book_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
public void getPublisher_id() {
return publisher_id;
}
public void setPublisher_id(long publisher_id) {
this.publisher_id = publisher_id;
}
@Override
public String toString() {
return "Book{" +
"book_id=" + book_id +
", name='" + name + '\'' +
", pblisher=" + publisher +
'}';
}
}
2)实体类Publisher
package entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name="publisher")
public class Publisher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "publisher_id")
private long publisher_id;
@Column(name = "name")
private String name;
/*
* cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
* CascadeType.all : 所有
* MERGE :更新
* PERSIST :保存
* REMOVE :删除
* 注意:开发中尽量不使用级联
*/
@OneToMany(mappedBy = "publisher")//放弃维护权,mappedBy为对方表配置的关系属性名称,cascade表示配置级联
private Set<Book> books=new HashSet<Book> ();
//注意:放弃维护调用getBooks().add()无效
public long getPublisher_id() {
return publisher_id;
}
public void setPublisher_id(long publisher_id) {
this.publisher_id = publisher_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
@Override
public String toString() {
return "Publisher{" +
"publisher_id=" + publisher_id +
", name='" + name + '\'' +
", books=" + books +
'}';
}
}
3)测试
import dao.BookDao;
import dao.PublisherDao;
import entity.Book;
import entity.Publisher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:ApplicationContex.xml")
public class test4 {
@Autowired
BookDao bookDao;
@Autowired
PublisherDao publisherDao;
@Test
@Transactional
@Rollback(false)
public void test(){//两张表各创建一条记录,并产生关联
Publisher publisher=new Publisher ();
publisher.setName ("人民出版社");
Book book=new Book ();
book.setName ("西游记");
book.setPublisher (publisher);
bookDao.save (book);
publisherDao.save (publisher);
}
@Test
@Transactional
@Rollback(false)
public void addOne(){//新增一本书,归属到id为1的出版社
Book book=new Book ();
book.setName ("白鹿原");
Publisher publisher=publisherDao.findOne (1L);
book.setPublisher (publisher);
bookDao.save (book);
}
}
10
多对多表
如:1一个用户可对应多个角色,1个角色可对应多个用户
1)角色表role
package entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private long id;
@Column(name = "name")
private String name;
/*
可放弃维护,可设置级联,与一对多类似,但一般情况下不放弃维护
*/
@ManyToMany(targetEntity = User.class)
@JoinTable(name="user_role",joinColumns = {//name为第三张表的表名
@JoinColumn(name="role_id",referencedColumnName = "role_id")//设置当前表在第三张表的外键,name为外键名,referencedColumnName指向当前表的id
},inverseJoinColumns = {
@JoinColumn(name="user_id",referencedColumnName = "user_id")//设置对方表在第三张表的外键
})
private Set<User> roles=new HashSet<User> ();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<User> getRoles() {
return roles;
}
public void setRoles(Set<User> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
", roles=" + roles +
'}';
}
}
2)用户表user
package entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private long id;
@Column(name = "name")
private String name;
@ManyToMany(targetEntity = Role.class)
@JoinTable(name="user_role",joinColumns = {
@JoinColumn(name="user_id",referencedColumnName = "user_id")
},inverseJoinColumns = {
@JoinColumn(name="role_id",referencedColumnName = "role_id")
})
private Set<Role> roles=new HashSet<Role> ();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", roles=" + roles +
'}';
}
}
3)测试
import dao.RoleDao;
import dao.UserDao;
import entity.Role;
import entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:ApplicationContex.xml")
public class test5 {
@Autowired
RoleDao roleDao;
@Autowired
UserDao userDao;
@Test
@Transactional
@Rollback(false)
public void test(){//建立一个用户和一个角色,并产生联系
Role role=new Role ();
role.setName ("管理员");
User user=new User ();
user.setName ("user1");
user.getRoles ().add (role);
roleDao.save (role);
userDao.save (user);
}
}
注意
在多对多表的实体类中,如果双方都定义了外键及包含外键的toString方法,在打印其中一个实体对象时,两个相关联的实体会调用对方的toString方法从而进入死循环导致栈溢出
11
对象导航查询
查询某个对象时,可通过该对象查询到与之相关联的其他对象
//核心代码
@Test
@Transactional//解决no session问题
public void test1(){
Role role=roleDao.findOne (1L);
Set<User> users=role.getUsers ();
for (User user:
users) {
System.out.println (user.getId ());
System.out.println (user.getName ());
}
}
一对多关系中:
一方查多方,延迟加载
多方查一方,立即加载
常见问题
1
使用JPQL进行更新、删除操作报错
Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
在jpa中所有更新、删除操作要求必须是在事务的前提下进行操作,所以要在持久化接口中@Transactional
标注事务
package com.example.gradle_springboot.mapper;
import com.example.gradle_springboot.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import javax.transaction.Transactional;
import java.util.List;
@Transactional//标注事务
public interface UserMapper extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
@Modifying
@Query(value = "update User set deleted=1 where id=?1")
void logicDeleteById(Integer id);
@Query(value = "from User where deleted=0")
List<User> findByIdNotDeleted();
}
2
查询数据报错
org.hibernate.LazyInitializationException: could not initialize proxy [com.example.gradle_springboot.entity.User#1] - no Session
网上很多解决方案,多是简单粗暴地在配置文件中将全局懒加载关掉。但这样的做法并不好,应该是在出问题的实体类上将该实体类的懒加载关掉,在出问题的实体类上加@Proxy(lazy = false)
注解
3
逻辑删除的实现
jpa中没有实现对逻辑删除的封装,需要手动去持久化接口中定义删除和查询方法
4
乐观锁的实现
在实体类定义一个属性version并在该属性上新增@Version
注解
package com.example.gradle_springboot.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import org.hibernate.annotations.Proxy;
import javax.persistence.*;
@Entity
@Table(name="user")
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" })
@Data
@Proxy(lazy = false)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
@Version
private Integer version=0;
//逻辑删除
private Integer deleted=0;
}
在进行每更新操作时会自增版本号
5
对save方法的理解
1)对于传入的实体类,如果有id,该操作为更新操作,否则为插入操作
2)当进行更新操作时,如果实体类并没有被修改,执行save方法时是不会去操作数据的
6
自动填充时间
1)在启动类上添加@EnableJpaAuditing,启动
2)在实体类添加@EntityListeners(AuditingEntityListener.class)
3)在字段上添加@CreatedDate,@LastModifiedDate
7
分页查询取不到数据问题
//省略部分代码
Pageable pageable=PageRequest.of(1,10);//第一页,每页10条数据(第一个参数应该是0)
Page<Subject> subjectPage=subjectResponsitory.findAll(pageable);
List<Subject> subjects=subjectPage.getContent();//取出的数据size为0,也就是没有取到数据
查看PageRequest的静态方法of的源码
//部分
/**
* Creates a new unsorted {@link PageRequest}.
*
* @param page zero-based page index, must not be negative. page参数从0开始
* @param size the size of the page to be returned, must be greater than 0.
* @since 2.0
*/
public static PageRequest of(int page, int size) {
return of(page, size, Sort.unsorted());
}
从源码上看,第一页时page应该为0,所以上面取不到数据的原因是:总共只有1页的数据,却取了第二页的数据
8
getOne()报错
package com.zhanghuan.scheduler;
import com.zhanghuan.scheduler.job.dao.ScheduleJobDao;
import com.zhanghuan.scheduler.job.dao.ScheduleJobLogDao;
import com.zhanghuan.scheduler.job.entity.ScheduleJobEntity;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SchedulerApplicationTests {
@Autowired
ScheduleJobDao scheduleJobDao;
@Test
void contextLoads() {
ScheduleJobEntity scheduleJobEntity = scheduleJobDao.getOne("2");//这一行报错
System.out.println(scheduleJobEntity);
}
}
如下错误
org.hibernate.LazyInitializationException: could not initialize proxy [com.zhanghuan.scheduler.job.entity.ScheduleJobEntity#2] - no Session
解决方法
package com.zhanghuan.scheduler;
import com.zhanghuan.scheduler.job.dao.ScheduleJobDao;
import com.zhanghuan.scheduler.job.dao.ScheduleJobLogDao;
import com.zhanghuan.scheduler.job.entity.ScheduleJobEntity;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SchedulerApplicationTests {
@Autowired
ScheduleJobLogDao scheduleJobLogDao;
@Autowired
ScheduleJobDao scheduleJobDao;
@Test
void contextLoads() {
//使用findById替换
ScheduleJobEntity scheduleJobEntity = scheduleJobDao.findById("2").get();
System.out.println(scheduleJobEntity);
}
}
9
数据库中数据时间不正确
代码中将new Date()
存入数据库,查看数据发现时间不正确,此问题是数据源中的url未配置正确导致,应配置为jdbc:mysql:///scheduler?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8