我们之前学习的JDBC本身也是一套规范,MySQL和Oracle等数据库公司根据规范开发驱动,我们便实现一套Java代码,便可操作数据库,在不改变Java代码的情况下,修改Driver驱动便可实现操作数据库
这里以MySQL为例子,回顾着写一下链接数据库的代码:
/**
* Statement方法
*/
//注册驱动
class.forName(com.mysql.jdbc.com);
//建立链接
Connection con = DriverManager.getConnection(url,user,password);
//创建对象
Statement st = createStatement();
//执行查询返回结果集
String sql = "select * from user_table";
Result rs = st.excuteQuery(sql);
/**
* PreparStatement方法
*/
String sql = "select * from user_table";
//注册驱动
class.forName(com.mysql.jdbc.com);
//建立链接
Connection con = DriverManager.getConnection(url,user,password);
//创建对象
PrepardStatement pst = con.preparStatement(sql);
pst.setString(1,);//从1编号,写每个占位符?的值
//执行查询返回结果集
Result result = pst.executeQuery();
试问Statement 和 preparStatement 的区别
方法区别在于preparStatement的提高性能 预处理 提高安全性
重点说下安全性 在登陆账号密码的时候 password稍加改动便可绕过验证直接登录
例如sql语句为:
select * from user_table where name = '' and password = ''
如果用户密码 password 输入的值为 ==’ or ’ 1 ’ = ’ 1 ==
则sql语句变为:
select * from user_table where name = '随意' and password = '' or ' 1 ' = ' 1 '
//则OR 1 =1 为 true 直接登陆成功
preparStatement方法用占位符的预处理避免了这个问题。
ORM : 对象关系映射
在面向对象开发的过程中的,通过ORM就可把对象映射到关系型数据库中,使程序能够做到建立对象和数据库的关联,用户操作实体类对象便可以操作数据库
建立两个映射关系
目的:
实现ORM思想的框架有
JPA规范由SUN公司定义的一套规范,内部由接口和抽象类组成。
实现JPA规范的框架有hibernate 和 toplink 等框架。hibernate除了作为ORM框架之外,也是一种JPA实现。我们以hibernate作为了解JPA规范的框架。
pom.xml
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Finalproject.hibernate.version>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-entitymanagerartifactId>
<version>${project.hibernate.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-c3p0artifactId>
<version>${project.hibernate.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.6version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.18version>
dependency>
dependencies>
JPA的核心配置文件位于:类路径下一个META-INF文件夹下。命名为:persistence.xml
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProviderprovider>
<properties>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="root"/>
<property name="javax.persistence.jdbc.diver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
properties>
persistence-unit>
persistence>
注意自动创建数据库表的三个命令区别:hibernate.hbm2ddl.auto
因为我使用了LomBok插件,所以省略了getter、setter、toString方法
Customer.java
/**
* 客户实体类
*/
@Data
public class Customer {
private Long custId;//主键
private String custName;//名称
private String custSource;//客户来源
private String custLevel;//客户级别
private String custIndustry;//客户所属行业
private String custPhone;//联系方式
private String custAddress;//地址
}
配置实体类和表
配置实体类中属性和表中字段
/**
* 客户实体类
*/
/**
* 第一步:配置实体类与表的映射关系
* Entity: 声明实体类
* Table: 实体类映射的表 name = "表名"
*/
@Data
@Entity
@Table(name = "cst_customer")
public class Customer {
/**
* 第二步:配置属性与表的字段名
* Id: 声明主键
* GeneratedValue 配置主键的生成策略
* (strategy = GenerationType.IDENTITY) 自增
* Column(name = "字段名")
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;//主键
@Column(name = "cust_name")
private String custName;//名称
@Column(name = "cust_source")
private String custSource;//客户来源
@Column(name = "cust_level")
private String custLevel;//客户级别
@Column(name = "cust_industry")
private String custIndustry;//客户所属行业
@Column(name = "cust_phone")
private String custPhone;//联系方式
@Column(name = "cust_address")
private String custAddress;//地址
}
各个注解在代码块中有注释。
操作步骤:
- 加载配置文件创建实体管理类工厂对象 Persistence.createEntityManagerFactory();
- 通过实体管理器工厂获取实体管理器 factory.createEntityManager();
- 创建并开启事务 em.getTransaction();
- CRUD
- 提交事务(回滚事务)tx.commit();
- 释放资源
加载配置文件创建实体管理类工厂对象 | Persistence.createEntityManagerFactory(); |
---|---|
通过实体管理器工厂获取实体管理器 | factory.createEntityManager(); |
创建 | entityManager.getTransaction(); |
开启事务 | transaction.begin(); |
CRUD | 见下表 |
提交事务(回滚事务) | transaction.commit(); |
释放资源 |
persist | 保存 |
---|---|
merge | 更新 |
remove | 删除 |
find | 查询(根据id) |
getReference | 查询(根据id) |
find 和 getReference 的区别:
find : 立即加载 凡调用到find方法便立即发送查询sql语句
getReference : 延迟加载 执行方法不发送sql语句 什么时候用到,什么时候发送查询语句
public class CustomerTest {
@Test
public void testSave(){
//1.加载配置文件创建工厂(实体管理类工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");//myJpa为配置文件设置的持久层名字
//2.通过实体管理类工厂获取实体管理器
EntityManager em = factory.createEntityManager();
//3.获取事务对象
EntityTransaction tx = em.getTransaction();
tx.begin();//开启事务
//4.完成增删改查--保存一个客户
Customer customer = new Customer();
customer.setCustName("奥里斯");
customer.setCustSource("CSDN");
customer.setCustIndustry("CSDNBLOG");
customer.setCustLevel("NO.1");
customer.setCustAddress("人民路");
customer.setCustPhone("1888888888");
em.persist(customer);//保存操作
//5.提交事务
tx.commit();
//6.释放资源
em.close();
factory.close();
}
}
由于每次执行持久层和数据库打交道的操作,便创建一次实体管理类工厂,这样的缺点是浪费资源和耗时
所以我们可以创建一个JpaUtils.java 利用静态代码块的方法实现只执行一次创建工厂的操作
JpaUtils.java
/**
* 创建公共的工厂对象
*
* 为了解决EntityManagerFactory实体管理器工厂的浪费资源和耗时问题
*/
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* 通过静态代码块的形式:当第一次访问此工具类的时候,创建一个公共的实体管理器工厂对象
* 第二次访问:直接通过一个创建好的公共工厂factory对象,创建实体管理器EntityManager
*/
public class JpaUtils {
private static EntityManagerFactory factory;
static {
factory = Persistence.createEntityManagerFactory("myJpa");
}
//工厂生产实体管理器
public static EntityManager getEntityManager(){
return factory.createEntityManager();
}
}
public void testSave(){
//创建实体管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//创建事务
EntityTransaction transaction = entityManager.getTransaction();
//开始事务
transaction.begin();
//创建客户对象 然后 保存操作
Customer customer = new Customer();
customer.setCustName("张峰");
customer.setCustSource("爱唱歌");
customer.setCustIndustry("我是歌手");
customer.setCustLevel("NO.2");
customer.setCustAddress("朝阳区");
customer.setCustPhone("1868666666");
//实体管理器保存实体
entityManager.persist(customer);
//提交事务
transaction.commit();
//释放资源
entityManager.close();
}
public void testUpdate(){
//创建实体管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//创建事务
EntityTransaction transaction = entityManager.getTransaction();
//开始事务
transaction.begin();
/**
* merge 方法更新
* 更新分为三步:
* 1 --> 先根据id查询,并赋值给实体类对象
* 2 --> 实体类set方法重新赋值需要更新的值
* 3 --> merge方法更新
*/
//1.查询
Customer customer = entityManager.find(Customer.class, 1l);
//2.赋值
customer.setCustName("三哥");
//3.merge方法更新
entityManager.merge(customer);
//提交事务
transaction.commit();
//释放资源
entityManager.close();
}
public void testDelete(){
//创建实体管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//创建事务
EntityTransaction transaction = entityManager.getTransaction();
//开始事务
transaction.begin();
/**
* remove 方法删除
* 删除分为两步:
* 1 --> 先根据id查询想要删除的数据 赋值给一个对象
* 2 --> 删除对象
*/
Customer customer = entityManager.find(Customer.class, 2l);
//remove删除
entityManager.remove(customer);
//提交事务
transaction.commit();
//释放资源
entityManager.close();
}
public void testFind(){
//创建实体管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//创建事务
EntityTransaction transaction = entityManager.getTransaction();
//开始事务
transaction.begin();
//创建客户对象 然后 查询
/**
* 查询find和getReference --> 根据id查询
* class: 查询数据的结果需要包装的实体类类型字节码
* 比如查询为顾客信息customer,结果类型封装为customer
* id: 主键值
*
* 方法区别:
* find: 立即加载 凡调用到find方法便立即发送查询sql语句
* getReference: 延迟加载 执行方法不发送sql语句 什么时候用到,什么时候发送查询语句
*
*/
Customer customer = entityManager.find(Customer.class, 1l);//id为long长整型 需要专为1L
System.out.println(customer);
//提交事务
transaction.commit();
//释放资源
entityManager.close();
}
jpql语句是操作实体类的
jpql语句与mysql语句相似
jpql 不支持select * ,但支持select 其他语句照搬然后微调
微调:表名修改为实体类名,字段名修改为实体类属性名
Jpql 语句是操作实体类的:
分为三步:
必须创建query对象,query对象才是执行jpql的对象
//查询---顺序
public void testSelect(){
//1. 创建实体管理器
EntityManager em = JpaUtils.getEntityManager();
//2. 创建事务 并开启
EntityTransaction ts = em.getTransaction();
ts.begin();
/**
* jpql由于不支持select * , 表名对应映射的实体类名
* mysql : select * from cst_customer;
* jpql : from Customer
*/
//3. 创建Query对象, query对象才是执行jpql的对象
String jpql = "from Customer";
Query query = em.createQuery(jpql);
//query.getResultList()方法查询结果并封装结果集为list
List list = query.getResultList();
//打印
for (Object obj : list
) {
System.out.println(obj);//由于结果集中包含实体类属性中各个类型 所以用Object上帝类
}
//4. 提交事务
ts.commit();
//5. 释放资源
em.close();
}
//查询---倒序
public void testSelectDesc(){
//1. 创建实体管理器
EntityManager em = JpaUtils.getEntityManager();
//2. 创建事务 并开启
EntityTransaction ts = em.getTransaction();
ts.begin();
/**
* mysql : select * from cst_customer order by cust_id desc ;
* jpql : from Customer order by custId desc
*/
//3. 创建Query对象, query对象才是执行jpql的对象
String jpql = "from Customer order by custId desc";
Query query = em.createQuery(jpql);
List list = query.getResultList();
for (Object obj : list
) {
System.out.println(obj);
}
//4. 提交事务
ts.commit();
//5. 释放资源
em.close();
}
/**
* JPQL与MySQL语句相似,
* mysql中表名对应jpql中的实体类名,mysql中的字段名对应jpql中的实体类属性
* jpql不支持select * 但是支持select
*/
//统计查询
public void testCount(){
/**
* mysql : select count(cust_id) from cst_customer;
* jpql : select count(custId) from Customer;
*/
EntityManager entityManager = JpaUtils.getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//创建query对象
String jpql = "select count(custId) from Customer";
Query query = entityManager.createQuery(jpql);
//因为count计数只有一个 并不是结果集 所以不用getResultList()
//query.getSingleResult() 结果为单一的方法
Object singleResult = query.getSingleResult();
System.out.println(singleResult);
//提交事务
transaction.commit();
//释放资源
entityManager.close();
}
//分页查询
public void testPage(){
/**
* mysql : select * from cst_customer limit 0,2;
* select * from cst_customer limit ?,?;
* jpql : from cst_customer limit ?,?
* ? 设置分页参数 jpql中的第二步
*/
//工厂创建实体管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//实体管理器创建事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();//开启事务
//1. 创建query对象
String jpql ="from Customer";
Query query = entityManager.createQuery(jpql);
//2. 设置参数
query.setFirstResult(0);//设置起始索引
query.setMaxResults(2);//设置单次查询条数
//3. 封装结果集
List resultList = query.getResultList();
//for each 方法遍历打印
// for (Object obj : resultList
// ) {
// System.out.println(obj);
// }
//迭代器方法遍历打印
Iterator<Object> it = resultList.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//提交事务
transaction.commit();
//释放资源
entityManager.close();
}
//模糊查询
public void testLike(){
/**
* mysql : select * from cst_customer where cust_name like '张%';
* jpql : from cst_customer where cust_name like ?
*/
//工厂创建实体管理器
EntityManager entityManager = JpaUtils.getEntityManager();
//实体管理器创建事务
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();//开启事务
//1. 创建query对象
String jpql ="from Customer where custName like ?";
Query query = entityManager.createQuery(jpql);
//2. 设置参数 query.setParameter(第几个占位符,"value值");
query.setParameter(1,"张%");
//3. 封装结果集
List resultList = query.getResultList();
//迭代器方法遍历打印
Iterator<Object> it = resultList.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//提交事务
transaction.commit();
//释放资源
entityManager.close();
}
封装结果集 | query.getResultList(); |
---|---|
单个数据结果 | query.getSingleResult(); |
设置占位符和参数 | query.setParameter(第几个占位符,“value值”); |
起始索引 | query.setFirstResult(0); |
最大索引(每页条数) | query.setMaxResults(15); |
Spring Data Jpa是Spring基于ORM框架,JPA规范的基础上封装的一套JPA应用框架。
Spring Data Jpa使得我们解脱了DAO层的操作,基本上所有的CRUD都可以依赖它实现。
实际的工作用推荐Spring Data Jpa + ORM完成。这样优点是切换不同的ORM方便,使数据层更加简单,方便解耦。
简化数据访问层代码,使用了Spring Data Jpa,我们致谢dao层接口,便自动具备了基本的CRUD和分页查询等操作
基于上面的客户表案例
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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>cn.itcastgroupId>
<artifactId>jpa-day2artifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<spring.version>5.0.2.RELEASEspring.version>
<hibernate.version>5.0.7.Finalhibernate.version>
<slf4j.version>1.6.6slf4j.version>
<log4j.version>1.2.12log4j.version>
<c3p0.version>0.9.1.2c3p0.version>
<mysql.version>5.1.6mysql.version>
properties>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.6.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-coreartifactId>
<version>${hibernate.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-entitymanagerartifactId>
<version>${hibernate.version}version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>5.2.1.Finalversion>
dependency>
<dependency>
<groupId>c3p0groupId>
<artifactId>c3p0artifactId>
<version>${c3p0.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-jpaartifactId>
<version>1.9.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>javax.elgroupId>
<artifactId>javax.el-apiartifactId>
<version>2.2.4version>
dependency>
<dependency>
<groupId>org.glassfish.webgroupId>
<artifactId>javax.elartifactId>
<version>2.2.4version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.18version>
dependency>
dependencies>
project>
配置Spring的配置文件,在类路径resource下,创建一个applicationContext.xml文件。配置spring data jpa与spring的整合
配置文件主要包括
下面配置文件有三个包扫描,意思分别是:
扫描实体类所在包 | name=“packagesToScan” value=“com.iyiliang.domain” |
---|---|
扫描dao所在的包 | base-package=“com.iyiliang.dao” |
扫描所有spring注解的包 | base-package=“com.iyiliang” |
applicationContext.xml
<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">
<bean id="entityManagerFactoty" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.iyiliang.domain" />
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
property>
<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" />
<property name="showSql" value="true" />
bean>
property>
<property name="jpaDialect" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
property>
bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root">property>
<property name="password" value="root">property>
<property name="jdbcUrl" value="jdbc:mysql:///jpa" >property>
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
bean>
<jpa:repositories base-package="com.iyiliang.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactoty" >jpa:repositories>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoty">property>
bean>
<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>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* cn.itcast.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
aop:config>
<context:component-scan base-package="com.iyiliang" >context:component-scan>
beans>
Customer.java
/**
* 客户实体类
*/
@Data
@Entity
@Table(name = "cst_customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;//主键
@Column(name = "cust_name")
private String custName;//名称
@Column(name = "cust_source")
private String custSource;//客户来源
@Column(name = "cust_level")
private String custLevel;//客户级别
@Column(name = "cust_industry")
private String custIndustry;//客户所属行业
@Column(name = "cust_phone")
private String custPhone;//联系方式
@Column(name = "cust_address")
private String custAddress;//地址
}
编写一个符合Spring Data Jpa 的dao层接口
只需要写dao层接口,不需要编写dao层接口的实现类
dao层接口规范:
基础CRUD接口 | JpaRepository<实体类类型, 主键类型> |
---|---|
分页等复杂功能接口 | JpaSpecificationExecutor<实体类类型> |
/**
* 客户实体类接口
*
*/
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
}
只写了这个dao接口 不写实现类,现在已经具备CRUD功能。下面简单的看几个吧。
用Junit4 首先建立test 测试 。然后因为交给了Spring容器完成所以指明@RunWith和@ContextConfiguration配置文件地址
/**
* 因为交给了Spring容器完成所以指明@RunWith和配置文件地址
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class CustomerDaoTest {
@Autowired
private CustomerDao customerDao;
}
查询通过ID。 如果需要通过其他属性查询,需自己在接口写方法,下面具体讲解。
//查询一个
@Test
public void testFindOne() {
Customer customer = customerDao.findOne(3l);//ID为long型
System.out.println(customer);
}
查询所有 结果为List集合
//查询所有(List集合)
@Test
public void testFindAll() {
List<Customer> all = customerDao.findAll();
for (Object obj: all
) {
System.out.println(obj);
}
}
保存
//测试保存方法 save()方法可用于新增和更新
@Test
public void testSave(){
//new一个客户
Customer customer = new Customer();
customer.setCustName("阿尔托");
customer.setCustPhone("011544886");
customer.setCustAddress("Alto City");
customer.setCustIndustry("Adventure冒险");
customer.setCustLevel("top1");
customer.setCustSource("推荐");
//保存这个客户信息
customerDao.save(customer);
}
修改信息 先查询后修改
/**
* save方法用于保存和更新
* 当数据库中没有该id的时候,执行保存操作
* 当数据库中有该id时候,用find先查到赋值为customer,再用set方法设置修改的值
*/
@Test
public void testUpadate(){
//查询一个客服赋值为customer
Customer customer = customerDao.findOne(6l);//id为long型
customer.setCustPhone("011544886");
customer.setCustAddress("昌平区");
customer.setCustSource("推荐");
//保存这个客户信息
customerDao.save(customer);
}
删除
//删除操作
public void testDelete(){
customerDao.delete(3l);//id为long型
}
jpql查询方式和sql类似。除了上面讲到的sql操作数据库表,jpql操作实体类对象。还有一点需要修改
@Query里面有个nativeQuery属性
nativeQuery | true | false(默认) |
---|---|---|
本地查询 | sql查询 | jpql查询(默认) |
如果返回客户信息是多个,需要list集合
对于一些内部没有定义的方法,比如上面只能通过ID查询。如果通过名字查询则自己定义。
1…写在dao接口
2…在新添加的方法上注解@Query(value = “jpql语句”)
下面的几个例子上面分别为dao接口里新增的方法!
下面的为Junit4单元测试用例!
例子1:
//根据姓名查询 from Customer where custName = ?
//因为需要返回一个客户信息 所以是customer类型
@Query(value = "from Customer where custName = ?")
public Customer findByName(String name);
//JPQL根据姓名查询
@Test
public void testFindByName(){
System.out.println(customerDao.findByName("卢哈哈"));
}
例子2:
/**
* 当有多个占位符时
* 一般参数顺序默认和占位符顺序保持一致
* 若顺序不想保持一致可以加索引(问号+数字)
*/
//根据姓名 手机号查询 from Customer where custName = ? and custPhone = ?
@Query(value = "from Customer where custName = ?2 and custPhone = ?1 ")
public Customer findByNameAndPhone(String phone, String name);
//根据姓名 手机号查询
@Test
public void testByNameAndPhone(){
Customer customer = customerDao.findByNameAndPhone("1888666666", "卢哈哈");
System.out.println(customer);
}
例子3:
/**
* @Query 为查询操作
* 要进行更新 需要加注解@Modifying
*/
//根据id修改姓名 update Customer set custName = ? where id = ?
//修改信息不用返回 所以void类型
@Modifying
@Query(value = "update Customer set custName = ?1 where custId = ?2")
public void updateNameById(String name,Long id);
//根据id修改姓名
@Test
@Transactional //添加事务支持
@Rollback(value = false) //默认回滚,所以设置回滚为"否"
public void testUpdateNameByName(){
customerDao.updateNameById("奥德赛",5l);
}
需要 注意 的是:
@Query执行的是查询
如果涉及到更新操作 ,需要加注解@Modifying
需要注意的是sql特有的查询返回的是list的数组,所以可用Object [] ,理解为一维数组
下面的几个例子上面分别为dao接口里新增的方法!
下面的为Junit4单元测试用例!
例子1:
//sql模糊查询188开头手机号
@Query(value = "select * from cst_customer where cust_phone like ? ",nativeQuery = true)
public List<Object[]> findByPhone(String phone);
//sql模糊查询188开头手机号
@Test
public void testSqlByPhone(){
List<Object[]> customers = customerDao.findByPhone("188%");
for (Object[] obj : customers
) {
System.out.println(Arrays.toString(obj));
}
}
例子2:
//查询所有
@Query(value = "select * from cst_customer " , nativeQuery = true)
public List<Object []> findAllBySql();
//sql查询所有
@Test
public void testFindAllBySql(){
List<Object[]> cou = customerDao.findAllBySql();
for (Object[] c: cou
) {
System.out.println(Arrays.toString(c));
}
}
前方高能!!!
方法名规则不写jpql语句,只写符合spring data jpa 方法名称规范的方法名,便自动可实现查询
写法为:findBy 开头代表查询 + 属性名称(首字母大写)+查询方式(默认为=,还有其他Like,IsNull,Not等)+ And(Or)…写法和sql相同
看下面两个例子:
//findBy + 属性名称 (按照属性名称完成匹配)
public Customer findByCustName(String name);
//findBy + 属性名称 + “查询方式(Like | IsNull 等等)”
//多条件查询加 And | Or 等
public List<Customer> findByCustLevelAndCustPhoneLike(String level, String phone);
上面学习的是基于接口JpaRepository
实现的CURD,现在学习第二个接口JpaSpecificationExecutor< T >
specification 规范 executor 执行人
JpaSpecificationExecutor 方法列表
Specification :查询条件
例子1:借助名字查询
//Spec按姓名查询
@Test
public void testSpecSelectByName(){
//匿名内部类
Specification<Customer> sp = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//root.get(按需求获取实体类对象属性)
Path<Customer> custName = root.get("custName");
//cb.equal(path对象,比较的值) 精准查询
Predicate cb = criteriaBuilder.equal(custName,"阿尔托");
//返回结果封装的cb
return cb;
}
};
//调用返回的结果
Customer one = customerDao.findOne(sp);
System.out.println(one);
}
例子2:
实现了多条件、模糊、排序三个函数
多条件和模糊组合查并按着ID排序
//多条件和模糊查询---行业精准NO.1和手机号模糊1888%
@Test
public void testMoreSpecAndLike(){
//创建Specification方法
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//第一个条件属性
Path<Customer> custLevel = root.get("custLevel");
//第二个条件属性
Path<Customer> custPhone = root.get("custPhone");
//取值
Predicate eq1 = criteriaBuilder.equal(custLevel, "NO.1");
/**
* like,gt,lt,等模糊查询方法与equal方法不同
* equal直接得到path对象(属性)进行比较;
* 模糊查询需要得到path,根据path指定参数类型比较
* 形式:cb.like(path.as(类型的字节码对象))
*/
Predicate eq2 = criteriaBuilder.like(custPhone.as(String.class),"188%");
// return eq2;
//合并条件eq1 eq2
Predicate and = criteriaBuilder.and(eq1,eq2);
return and;
}
};
/**
* 排序
* 实例化sort对象 并传入两个参数
* 第一个: 正序 Sort.Direction.ASC | 倒序 Sort.Direction.DESC
* 第二个 : 排序的属性名称
*/
Sort sort = new Sort(Sort.Direction.ASC,"custId");
List<Customer> all = customerDao.findAll(spec,sort);
for (Object obj : all
) {
System.out.println(obj);
}
}
例子3,分页
/**
* 分页
* 创建Pageable对象
*
* PageRequest(起始, 每页条数);
*/
@Test
public void tesPage(){
Specification<Customer> spec = null ;
Pageable pageable = new PageRequest(0, 3);
Page<Customer> pg = customerDao.findAll(spec, pageable);
System.out.println(pg.getContent());//得到数据集合列表
// System.out.println(pg.getSize()); //每页长度
System.out.println(pg.getTotalElements());//得到总条数
System.out.println(pg.getTotalPages());//得到总页数
}
ps:个人觉得没有方法名和自定义方法用jpql语句简单。。。
分析步骤:
假设客户为一个公司为和 联系人为员工 形成 一对多。
步骤
//配置客户和联系人之间的关系(一对多关系)
/**
* 使用注解的形式配置多表关系
* 1.声明关系
* @OneToMany : 配置一对多关系
* targetEntity :对方对象的字节码对象
* 2.配置外键(中间表)
* @JoinColumn : 配置外键
* name:外键字段名称
* referencedColumnName:参照的主表的主键字段名称
*
* * 在客户实体类上(一的一方)添加了外键了配置,所以对于客户而言,也具备了维护外键的作用
*
*/
@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
//多
/**
* 配置联系人到客户的多对一关系
* 使用注解的形式配置多对一关系
* 1.配置表关系
* @ManyToOne : 配置多对一关系
* targetEntity:对方的实体类字节码
* 2.配置外键(中间表)
*
* * 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
*
*/
@ManyToOne(targetEntity = Customer.class,fetch = FetchType.LAZY)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
由于双方都配置了联系,建立了双向链接。会发送两条insert语句,一条多余的update语句,那我们的解决是思路很简单,就是一的一方放弃维护权。
一般一的一方放弃。直接在注解上修改
/**
*放弃外键维护权的配置将如下配置改为
*/
//@OneToMany(targetEntity=LinkMan.class)
//@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
//设置为
@OneToMany(mappedBy="customer")
假设
两张表分别为 用户表 和 角色表
步骤
/**
* 配置用户到角色的多对多关系
* 配置多对多的映射关系
* 1.声明表关系的配置
* @ManyToMany(targetEntity = Role.class) //多对多
* targetEntity:代表对方的实体类字节码
* 2.配置中间表(包含两个外键)
* @JoinTable
* name : 中间表的名称
* joinColumns:配置当前对象在中间表的外键
* @JoinColumn的数组
* name:外键名
* referencedColumnName:参照的主表的主键名
* inverseJoinColumns:配置对方对象在中间表的外键
*/
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
@JoinTable(name = "sys_user_role",
//joinColumns,当前对象在中间表中的外键
joinColumns = {
@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
//inverseJoinColumns,对方对象在中间表的外键
inverseJoinColumns = {
@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}
)
private Set<Role> roles = new HashSet<>();
级联操作:指操作一个对象同时操作它的关联对象
使用方法:
在注解上配置cascade = “persist | remove | merge | all ”
这几个词和jpa操作数据库意思一样
@OneToMany(mappedBy="customer",cascade=CascadeType.ALL)
通过已加载的对象,查询他的关联对象
查询一个客户,获取该客户下的所有联系人
@Autowired
private CustomerDao customerDao;
@Test
//由于是在java代码中测试,为了解决no session问题,将操作配置到同一个事务中
@Transactional
public void testFind() {
Customer customer = customerDao.findOne(5l);
Set<LinkMan> linkMans = customer.getLinkMans();//对象导航查询
for(LinkMan linkMan : linkMans) {
System.out.println(linkMan);
}
}
延迟加载与立即加载
/**
* 在联系人对象的@ManyToOne注解中添加fetch属性
* FetchType.EAGER :立即加载
* FetchType.LAZY :延迟加载
*/
@ManyToOne(targetEntity=Customer.class,fetch=FetchType.EAGER)
@JoinColumn(name="cst_lkm_id",referencedColumnName="cust_id")
private Customer customer;
默认情况下,一关联多采用延迟加载。多连一采用立即加载