导入依赖
<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.18</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>
<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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</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>
<!-- SpringDataJpa核心包 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.0.6.RELEASE</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.编写/resources/applicationContext.xml文件
头文件
<?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">
<beans ...>
<!-- 数据源配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="jdbc:mysql:///springdatajpa?characterEncoding=UTF8"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--- Spring整合JPA配置 *** -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- Jpa具体实现的提供商 : Hibernate-->
<property name="jpaVendorAdapter">
<!-- Hibernate实现 -->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- 控制台打印sql语句 -->
<property name="showSql" value="true"></property>
<!-- 自动维护表 -->
<property name="generateDdl" value="true"></property>
</bean>
</property>
<!-- 使用Spring扫描实体类 -->
<property name="packagesToScan" value="com.osc.pojo"></property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<!-- 注入entityManagerFactory -->
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!-- Spring Data JPA扫描Dao接口配置 ***-->
<jpa:repositories base-package="com.osc.dao"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager">
</jpa:repositories>
</beans>
3.编写实体类并进行JPA注解映射
com.osc.pojo.User.java
@Entity //映射类,必须的
@Table(name = "user") //映射表, name: 表名称
public class User implements Serializable{
@Id // 映射主键字段
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id") // 映射普通字段,name:字段名称
private Long id;
@Column(name = "name")
private String name;
@Column(name = "age")
private Integer age;
//已省略getter和setter....
}
4.编写符合规范的DAO层接口
com.osc.dao.UserDao
/**
* JpaRepository<实体类类型,主键类型>接口有CRUD,分页,排序的功能
* JpaSpecificationExecutor<实体类类型>拥有动态条件查询
*/
public interface UserDao extends
JpaRepository<User,Long>,JpaSpecificationExecutor<User>{
}
5.进行CRUD操作(CrudRepository接口使用)
-
测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class Demo1 { @Autowired private UserDao userDao; //TODO }
添加数据
User user = new User();
user.setName("小小");
userDao.save(user);
修改数据
User user = new User();
// 必须传入数据库存在ID,否则无视id创建数据
user.setId(1L);
user.setName("小小");
userDao.save(user);
删除数据
userDao.deleteById(1L);
查询所有
List<User> list = userDao.findAll();
查询一条数据
//注意:spring data jpa2.0以前,使用findById(没有get)
User user = userDao.findById(1L).get();
扩展
JPQL语句
JPQL的语法和SQL语法非常像。JPQL用类名代替表名,JPQL的类的属性名代替字段名称
-
查询全部
/* User表名对应的类名 u:类的别名 */ select u from User u --不能写成 select * from Cust(错误写法)
-- 可简写 from User
等价于 : select * from user
-
查询指定列
select u.age,u.name from User u
select new User(u.age,u.name) from User u
等价于 : select age,name from user
-
排序
from User order by id desc
等价于 : select * from user order by id desc
-
统计查询
-- 还可以使用 max(),min(),avg(),sum() select count(*) from User
等价于 : select count(*) from user
Repository接口-基于@Query进行JPQL操作
基于com.osc.dao.UserDao开发,方法都写在这个接口里面
-
查询操作
//value: 编写JPQL语句 @Query(value = "from User where name = ?1") public List<User> queryName(String name); //查询总记录数 @Query(value = "select count(*) from User") public Long selectCount();
-
更新操作(必须在方法或者类上加@Transactional注解)
@Modifying // 注意:如果使用@Query来进行更新操作,必须加上@Modifying @Transactional @Query("update Customer set custAge = ?1 where custId = ?2") public void updateCustAage(Integer custAge,Long custId);
-
删除操作
//nativeQuery: 是否执行SQL语句,true:执行SQL,false:执行JPQL(默认值) @Modifying @Transactional @Query(value = "delete from cst_customer where cust_name = ?1",nativeQuery =true ) public void deleteByName(String custName);
Repository接口-根据方法命名规则查询
好处:不用写@Query注解和SQL或JPQL语句,可以直接调用操作数据库
方法要求:
- 以findBy开头
- findBy后面跟上查询的属性名称
- 属性后面跟上查询关键词
- 整个方法遵守驼峰式命名
List<User> findByNameEquals(String name);
//如果是Equals,可以省略的
List<User> findByName(String name);
/* from User where name = ?1 */
//模糊查询
List<User> findBytNameLike(String name);
/* from User where name like ?1 */
//比较
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeGreaterThanEqual(Integer age);
/* from User where age >= ?1 */
//条件连接
List<User> findByNameLikeAndAgeGreaterThan(String name,Integer age);
/* from User where name = ?1 and age >= ?2 */
List<User> findByNameLikeOrAgeGreaterThan(String name,Integer age);
/* from User where name = ?1 or age >= ?2 */
PageAndSortingRepository接口的使用
分页查询
@Test
public void test1(){
int page = 1; //当前也
int size = 2; //页大小
//Pageable: 用于 封装分页参数对象 注意:Pageable的page参数,从0开始计算的
Pageable pageable = PageRequest.of(page-1,size);
//Page: 封装分页查询后的结果的对象 例如:总页数,总记录数,当前页数据列表
Page<User> pageData = userDao.findAll(pageable);
//取出分页后的结果
System.out.println("总记录数:"+pageData.getTotalElements());
System.out.println("总页数:"+pageData.getTotalPages());
//当前页数据列表
List<User> list = pageData.getContent();
}
排序查询
@Test
public void test2(){
//Sort: 封装所有的排序条件
//Order: 参数一:排序方向 DESC/ASC 参数二:需要排序的属性名称
//单列排序
Sort sort = Sort.by(
new Sort.Order(Sort.Direction.DESC,"custId"));
//多列排序
Sort sort = Sort.by(
new Sort.Order(Sort.Direction.DESC,"id"),
new Sort.Order(Sort.Direction.ASC,"age"));
List<User> list = (List<User>)userDao.findAll(sort);
}
混合查询
@Test
public void test3(){
int page = 1;
int size = 2;
Sort sort = Sort.by(new Sort.Order(Sort.Direction.DESC,"id"));
Pageable pageable = PageRequest.of(page-1,size,sort);
Page<User> pageData = userDao.findAll(pageable);
System.out.println("总记录数:"+pageData.getTotalElements());
System.out.println("总页数:"+pageData.getTotalPages());
List<User> list = pageData.getContent();
}
Specification动态查询
root 根对象,用于查询对象的属性
query JPA原生对象,保留接口
cb 条件构造器,用于构造各种条件(例如,等于,大于,与,或等)
单条件查询
//Specification: 封装动态条件的对象
Specification<User> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
// 精确查询 sql: name = 'osc'
Predicate pre = cb.equal(root.get("name"),"osc");
// 模糊查询 sql: name like '%o%'
Predicate pre = cb.like(root.get("name"),"%o%");
// 比较查询 sql: age > 18
Predicate pre = cb.gt(root.get("age").as(Integer.class), 18);
return pre;
}
};
List<User> list = userDao.findAll(spec);
多条件查询
Specification<User> spec = new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate pre1 = cb.like(root.get("name"),"%o%");
Predicate pre2 = cb.gt(root.get("age").as(Integer.class), 18);
//拼接条件
return cb.and(pre1,pre2);
}
};
List<User> list = userDao.findAll(spec);
String name = "osc";
Integer age = 20;
Specification<User> spec = (Specification) (root, query, cb) -> {
//创建动态条件集合
List<Predicate> pres = new ArrayList<>();
if(StringUtils.isNoneBlank(name)){
pres.add(cb.like(root.get("name"),"%o%");
}
if(StringUtils.isNoneBlank(age)){
pres.add(cb.gt(root.get("age").as(Integer.class), 18));
}
//拼接条件
Predicate[] preArr = new Predicate[pres.size()];
return cb.and(pres.toArray(preArr);
};
List<User> list = userDao.findAll(spec);
动态条件+分页+排序
...
Pageable pageable = PageRequest.of(page-1, size, sort);
Page pageData = user.findAll(spec, pageable);
多表操作
1-N / N-1(一对多/多对一)
- 创建客户实体类(1)
@Entity
@Table(name = "cst_customer")
public class Customer implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
@Column(name = "cust_name")
private String custName;
//维护联系人关系
// 1) Set集合不能为null,必须初始化
// 2) 初始化元素为0
private Set<LinkMan> linkmans = new HashSet<>(0);
//均需实现getter和setter....
}
- 创建联系人实体类(N)
@Entity
@Table(name = "cst_linkman")
public class LinkMan implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "lkm_id")
private Long lkmId;
@Column(name = "lkm_name")
private String lkmName;
//维护客户关系
//Customer不需要初始化
private Customer customer;
//均需实现getter和setter....
}
-
一对多映射
一方(1):需加入@OneToManay
多方(N):需加入@ManyToOne
@JoinColumn,外键映射,两方都可以配置,但是效率较低,采用放弃一方维护权
放弃一方维护权:去掉@JoinColumn注解,并在@XxxToXxx中加入mappedBy吧维护权给对方
客户
/**
* @OneToMany
* targetEntity: 对方的类型
* @JoinColumn: 映射外键
* name: 外键字段名称(不能省略)
* referencedColumnName: 外键参考的主键字段名称(可以省略)
* mappedBy: 把外键维护权交给对方,对方的属性名称(对方实体类 private Customer customer)
* cascade: 级联操作
* CascadeType.PERSIST: 级联添加
* CascadeType.REMOVE: 级联删除
* CascadeType.ALL: 所有级联操作
*/
@OneToMany(targetEntity = LinkMan.class,
mappedBy = "customer",cascade = CascadeType.PERSIST)
//放弃维护权
/*@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")*/
private Set<LinkMan> linkmans = new HashSet<>(0);
联系人(维护方)
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
添加数据
修改删除必须添加 @Transactional @Rollback(false)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo1 {
@Autowired
private CustomerDao customerDao;
@Autowired
private LinkManDao linkManDao;
@Test
@Transactional
@Rollback(false)
public void test1(){
//1.创建1个客户,创建两个联系人
Customer c = new Customer();
c.setCustName("osc");
LinkMan lkm1 = new LinkMan();
lkm1.setLkmName("小红");
LinkMan lkm2 = new LinkMan();
lkm2.setLkmName("小翠");
//2.建立客户与联系人关系
//客户->联系人
c.getLinkmans().add(lkm1);
c.getLinkmans().add(lkm2);
//联系人->客户
lkm1.setCustomer(c);
lkm2.setCustomer(c);
//3.保存客户,保存联系人
customerDao.save(c);
//添加了级联添加不需要分开插入数据
//linkManDao.save(lkm1);
//linkManDao.save(lkm2);
}
}
N-N(多对多)
用户表
这里是用@JoinTable不是@JoinColumn
检查applicationContext.xml是否添加了维护表的配置,否则不能创建中间表
<!--Jpa具体实现的提供商--> <property name="jpaVendorAdapter"> <!--Hibernate实现--> <bean class="HibernateJpaVendorAdapter" ...> ... <!--自动维护表--> <property name="generateDdl" value="true"></property> </bean> </property>
@Entity
@Table(name = "sys_user")
public class SysUser implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;
@Column(name = "user_name")
private String userName;
//维护角色关系
/**
* @ManyToMany 多对多
* targetEntity: 对方类型
* @JoinTable: 映射中间表
* name: 中间表表名称
* joinColumns: 当前方在中间表定义外键字段名称
* inverseJoinColumns: 对方在中间表定义外键字段名称
*/
@ManyToMany(targetEntity = SysRole.class)
@JoinTable(name = "user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<SysRole> roles = new HashSet<>(0);
}
角色表
把维护权给用户是防止删除用户时把角色表的数据使用级联删除也一起删了
@Entity
@Table(name = "sys_role")
public class SysRole implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
@ManyToMany(targetEntity = SysUser.class,mappedBy="roles")
/*@JoinTable(name = "user_role",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "user_id"))*/
private Set<SysUser> users = new HashSet<>(0);
}
中间表user_role会自动生成
对象导航查询
延时加载
加载只需要用到的数据,不加载有其他关系的数据,提高数据库性能
/**
* fetch : FetchType.LAZY 延时加载
*/
@ManyToOne(targetEntity = Customer.class,fetch = FetchType.LAZY)