搭建案例框架所需配置:
pom.xml 文件
<properties>
<spring.version>4.2.4.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>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</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>
<!-- 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>
<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>4.2.4.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>
spring配置文件:
<?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">
<!-- 1.dataSource 配置数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jpa331" />
<property name="user" value="root" />
<property name="password" value="123" />
</bean>
<!--工厂类对象-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--实体类的包扫描器-->
<property name="packagesToScan" value="com.itheima.jpa.entity"/>
<!--配置供应商适配器-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--是否向控制台输出sql语句-->
<property name="showSql" value="true"/>
<!--是否自动创建表
如果是true:相当于update,hibernate.hbm2ddl.auto属性配置成update
如果是false:相当于none
-->
<property name="generateDdl" value="false"/>
<!--使用的数据库类型-->
<property name="database" value="MYSQL"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<!--update在现有表的基础上,进行修改,改为Create,就是jpa重新创建表-->
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!--配置事务-->
<!-- 3.事务配置-->
<!-- 3.1JPA事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 3.2.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>
<!-- 3.3.aop-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.itheima.jpa.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
</aop:config>
<!--dao的包扫描器-->
<jpa:repositories base-package="com.itheima.jpa.dao"
transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"/>
</beans>
一、表和表之间的关系
1、一对一的关系
A、B两张表,A表中的每条记录都对应B表的一条记录,反之亦然。
2、一对多关系
A、B两张表,A表中的每条记录都对应B表中的多条记录。B表中的多条对应A表中的一条记录。
3、多对多的关系
A、B两张表。A表中的每条记录对应B表中的多条,B表中的每条记录对应A表中的多条记录。
需要有一个中间表C记录多对多的关系。
————————————————————————————————
3、外键方案
使用客户表和客户扩展表。
配置关联关系:
1)在Customer实体类中添加一个属性CustomerExt类型
2)在属性上添加一个@OneToOne代表是一个一对一的关系
3)在属性上添加一个@JoinColumn
name:存储外键的字段的名称
referencedColumnName:对方表的主键字段的名称(可以不用配置)
测试:
1)创建一个Customer对象
2)创建一个CustomerExt对象
3)配置对象的关联关系。
4)把对象写入数据库。
插入数据应该开启事务。可以使用@Transactional注解开启事务。
无需在两个表中都添加外键。只需要在一个表中记录外键即可。
可以在一方放弃维护权:
删除@JoinColumn注解
在@OneToOne注解中添加mappedBy属性,值应该是对方实体类维护关联关系属性的名称。
@OneToOne(mappedBy = “ext”)
4、级联操作
需要在@OneToOne注解中添加一个属性cascade:
CascadeType.PERSIST:级联添加
CascadeType.MERGE:级联更新
CascadeType.REMOVE:级联删除
CascadeType.ALL:增删改都级联处理。
5、主键方案
使用方法:
不使用@JoinColumn注解
使用@PrimaryKeyJoinColumn注解,不需要配置属性。
双方都需要添加
——————————————————————————————
1对1案例:顾客实体类
//代表是一个jpa的顾客实体类
@Entity
//配置实体类和数据库中表的映射关系 name对应的表名
@Table(name = "cst_customer")
public class Customer {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
//配置属性和字段的映射关系
@Column(name = "cust_id")
private long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_address")
private String custAddress;
@Column(name = "cust_phone")
private String custPhone;
//表示是一对一的关联关系
// 需要在@OneToOne注解中添加一个属性cascade:
// CascadeType.PERSIST:级联添加
// CascadeType.MERGE:级联更新
// CascadeType.REMOVE:级联删除
// CascadeType.ALL:增删改都级联处理。
@OneToOne(cascade = CascadeType.ALL)
//连接的字段
//name:当前表存储外键字段的名称
//@JoinColumn(name = "extid", referencedColumnName = "ext_id")
//使用主键关联(推荐,就是在1对1关联表中的对应属性添加 @PrimaryKeyJoinColumn 就行)
@PrimaryKeyJoinColumn
private CustomerExt ext;
get,set省略。。。。
对应的顾客使用产品表
@Entity
@Table(name = "cst_customer_ext")
public class CustomerExt {
@Id
@Column(name = "ext_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long extId;
private String memo;
private String info;
//对方实体类中对应关系的属性的名称
@OneToOne/*(mappedBy = "ext")*/
//@JoinColumn(name = "custid")
@PrimaryKeyJoinColumn //互有属性,都写这个主键注解(推荐)
private Customer customer;
get,set省略。。。。
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class CustomerTest {
@Autowired
private CustomerDao customerDao;
@Autowired
private CustomerExtDao customerExtDao;
@Test
@Transactional
@Commit
public void oneToone(){
//组装顾客用的产品对象。
CustomerExt customerExt = new CustomerExt();
//组装Customer顾客对象。
Customer customer = new Customer();
//--------不用设置主键,主键是自增长的----------------------------------
customer.setCustName("小明");
customer.setCustAddress("北京");
//设置这个产品属于哪个顾客对象
customer.setExt(customerExt);
//设置这个产品属于哪个顾客对象
customerExt.setCustomer(customer);
customerExt.setMemo("产品1");
//表间关系1对1,测试级联保存,和JPA创建表
customerDao.save(customer);
}
}
1对多表间关系:
2、关联关系的配置
一的一方:
1)添加一个属性记录多的一方的信息。应该是一个集合属性。
2)在属性上添加一个注解@OneToMany
3)使用@JoinColumn注解配置外键关系。
一定是多的一方记录外键,参照一的一方的主键。
多的一方:
1)在联系人实体类中添加一个Customer属性。
2)在属性上添加注解@ManyToOne
3)在属性上添加@JoinColumn注解
3、测试
1)每个实体类对应创建dao
2)在测试方法中,创建一个Customer2对象
3)创建LinkMan对象,可以创建多个。
4)配置客户和联系人之间的关系。
5)使用dao把数据写入数据库。
需要开启事务
只需要在一方维护关联关系即可,一方放弃维护。
一对多关系应该在一的一方访问维护交给多的一方来维护。
删除@JoinColumn注解
在@OneToMany注解中配置mappedBy属性
4、级联操作
应该只对主表进行操作即可,从表自动更新。
要求应该在@OneToMany注解中配置cascade属性
/**一对多中的,多方实体类。
* 联系人的实体类(数据模型)
*/
@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;
@Column(name="lkm_gender")
private String lkmGender;
@Column(name="lkm_phone")
private String lkmPhone;
@Column(name="lkm_mobile")
private String lkmMobile;
@Column(name="lkm_email")
private String lkmEmail;
@Column(name="lkm_position")
private String lkmPosition;
@Column(name="lkm_memo")
private String lkmMemo;
//配置,多对1,和懒加载
@ManyToOne(fetch = FetchType.LAZY)
//custid,关联1方的主键属性名,cust_id,关联数据库表的主键列
@JoinColumn(name = "custid", referencedColumnName = "cust_id")
private Customer2 customer;
//代表是一个jpa的实体类,一对多中的1方实体类。
@Entity
//配置实体类和数据库中表的映射关系 name对应的表名
@Table(name = "cst_customer2")
public class Customer2 {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
//配置属性和字段的映射关系
@Column(name = "cust_id")
private long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_address")
private String custAddress;
@Column(name = "cust_phone")
private String custPhone;
//mappedBy = "customer",让多的一方中的customer 属性来维护一的一方,相关联
// cascade = CascadeType.ALL 级联增删改,fetch = FetchType.LAZY,懒加载
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
//JoinColumn配置关联的字段信息
//name:外键字段的名称
//referencedColumnName:参照一的一方的主键字段名称
//@JoinColumn(name = "custid", referencedColumnName = "cust_id")
private Set<LinkMan> linkMans = new HashSet<>();
五、多对多关联关系
1、场景
我们采用用户、角色的关系为例
需要第三张表记录关联关系。
2、配置关联关系
多对多的关联关系使用注解@ManyToMany配置。
1)在实体类中添加一个集合属性。
2)在属性上添加@ManyToMany注解。
3)使用@JoinTable注解配置关联关系。
@JoinTable注解就是中间表的定义。
name:中间表的名称
当前表和中间表的映射关系。
对方表和中间表的映射关系
************************************************************
在多对多关联关系中,只能一方维护关联关系,另外一方放弃维护权。
************************************************************
//相当于中间表的定义
//name:中间表的表名
@JoinTable(name = "sys_user_role",
joinColumns = @JoinColumn(
//中间表和当前表映射的字段的名称
name = "userid",
//参照的当前表的主键字段
referencedColumnName = "user_id"),
inverseJoinColumns = @JoinColumn(
name = "roleid",
referencedColumnName = "role_id"))
3、测试
对应每个实体类创建一个dao
创一个测试类
1)创建两个用户
2)创建两个角色
3)配置用户和角色之间的关联关系
4)把用户和角色写入数据库
需要开启事务
4、放弃维护权
删除@JoinTable注解(需要其中一方放弃维护权)
在@ManyToMany注解中添加mappedBy属性
5、配置级联操作
在@ManyToMany注解中配置cascade属性。
六、关联查询
1、使用导航的方式查询
使用“对象.属性”方式进行查询。
****对多的查询默认是延迟加载。
如果想修改默认行为:
在@OneToMany注解中添加一个属性fetch
FetchType.LAZY:延迟加载
FetchType.EAGER:及时加载
通过联系人查询客户:
****对一的查询默认是及时加载。
也可以配置成延迟加载。
对多的查询默认都是延迟加载,对一的查询默认都是即时加载,可以修改fetch属性修改默认行为。
2、使用Specification方式查询
1)需求
查询客户信息,根据联系人的手机号。
2)sql
SELECT
*
FROM
cst_customer2 a
LEFT JOIN cst_linkman b ON a.cust_id = b.custid
WHERE
b.lkm_phone = '13344556678'
需要使用Customer2Dao执行查询。需要继承JpaSpecificationExecutor接口。
//使用root关联联系人表
//参数1:Customer2实体类中关联的属性名称
//参数2:连接方式
Join<Object, Object> join = root.join("linkMans", JoinType.LEFT);