1.1持久化,DAO与ORM
介绍持久化,DAO与ORM三个概念,以及它们之间的关系。
1.1.1
持久化
数据在计算机中有两种状态:
瞬时状态:保存在内存的程序数据,程序退出后,数据就消失了,称为瞬时状态
持久状态:保存在磁盘上的程序数据,程序退出后依然存在,称为程序数据的持久状态。持久化的数据保存在各种文件中,这些文件有不同的后缀,表示不同的类型,这些不同后缀的文件,需要不同的软件读取它。
持久化:将程序数据在瞬时状态和持久状态之间相互转换的机制
图一:持久化
现在的项目大多使用三层构架,即:表示层,业务逻辑层(服务层),持久化层。这里的持久化层就是持久化的意思。
1.1.2
DAO
DAO的全称为Data Access Object(数据访问对象)。
DAO与持久化的关系是:DAO一种程序数据访问层(持久化层)的设计思想。
现在我们做的项目,数据源都为数据库,也就是说我们把数据都保存在数据库软件中。但有时候我们需要把数据保存在其它类型数据源中,如保存在一个xml文件中,或者保存在一个txt文件中。即便是保存在数据库中,我们访问的方式也有很多种,如全部使用Statement对象来完操作,或者全部使用PreparStatement来完成。并且项目需要在多种数据源之间切换,DAO模式就可以很好的解决这个问题。
DAO不仅仅是其中的DAO类, DAO完整组件如下:
? DAO 工厂类 (暂时不使用,使用工厂设计模式)
? DAO接口(一个)
? 实现了DAO接口的具体类 ,根据数据源或者数据访问方式的不同有一个或多个
? 数据传输对象(DTO),数据对象(VO),实体类,POJO。第一个:实现Serializable接口;第二,添加equals方法。
图二:DAO
DAO接口中声明常用的保存查询等方法,这样利用面向接口编程,业务逻辑层声明接口,提高程序的可扩展性。
现在我们开发应用程序主要的数据源是数据库,但它还有可能是如XML文件,文本文件等这样的系统,不同的数据源存在不同的访问方式,即便是相同的数据源也存在不同的访问方式。DAO实现类针对不同的数据源编写不同的实现(Impl),即便是相同的数据源,针对不同的访问方式也可以编写不同的实现。
DAO工厂类根据不同的数据源与访问特性生产对应的DAO产品,供业务逻辑类使用。
值对象就是一个实体类,封装一条数据。
1.1.3
ORM
ORM的全称为Object-Relational Mapping(对象关系映射)。
ORM与DAO的关系是:ORM是DAO接口针对关系型数据库的一种实现,本质上就是一个DAO实现类。
现在我们开发的项目数据源大多为关系数据库,而在应用程序中表示数据的为某个实体类对象;利用程序我们把一个实体类的数据转化为关系型数据库表中的一行数据,或者把关系型数据库表中的一行数据转化为应用程序中的一个实体类,我们把:
“完成应用程序对象数据到关系型数据映射的机制称为对象-关系映射”
图三:ORM
1.2 Hibernate
ORM,MVC都是思想。Struts1是MVC思想的一个实现框架;hibernate是ORM思想的一个实现框架。
我们可以自己编写ORM的实现,其实在使用hibernate前,只要你编写的程序中有实体类,那你的开发就可以理解为已经使用了ORM了。
当然,自己编写各种实现使我们将大量的精力耗费在无止境繁琐的DAO编写中,这样有ORM框架出现帮我们处理这些繁琐的代码。
下面是一些出名常用的ORM框架:
? Hibernate http://www.hibernate.org
? iBatis http://www.ibatis.com
? Apache OJB http://db.apache.org/ojb/
? TopLink http://www.oracle.com/toplink/overview
? Castor JDO http://www.castor.org/jdo.html
我们把Hibernate等框架称为ORM思想的一个个优雅实现(呕吐中,这不是旺旺老师杜撰的名称,其实好多资料一直这么称呼),优雅原因:
? 不需要编写复杂的SQL语句
? 自动交换数据
? 免去繁琐的rs到实体类,或实体类到sql的转换
? 数据库对用户来说是透明的,不用关心底层的数据库,专注与OO领域的业务实体。
02Hello Hibernate
2.1
产生背景
Hibernate之父:Gavin
King
JBoss核心成员之一
EJB3.0专家委员会成员
《Hibernate In Action》作者
2001年开始开发Hibernate
2003年Hibernate发展为Java
世界主流持久层框架
充满激情
脾气倔强
永不言败
2.2 开发步骤
2.2.1 设置数据连接
使用hibernate,添加javaPrj即可。也就是说hibernate运行并不需要webApp支持。
2.2.2 添加hibernate支持
添加hibernate支持会完成以下两件事。
2.2.2.1 添加程序运行所需jar包
2.2.2.2 生成hibernate主配置文件
Hibernate配置文件分为两种,主配置文件与映射文件。
与Struts类似,hibernate也有xml的配置文件。名字为hibernate.cfg.xml(名称是hibernate.cfg,后缀为xml)。默认在src文件夹下。这个就是主配置文件,主配置文件有且仅有一个。
它包含三方面信息
1, 连接数据库的信息
2, 自己的属性配置
3, 映射文件的位置。
<hibernate-configuration> <session-factory> <property name="dialect">方言 </property> <property name="connection.url"> </property> <property name="connection.username">sa</property> <property name="connection.driver_class"> SQLServerDriver</property> </session-factory> </hibernate-configuration> |
方言,我们连接的各种不同数据库存在着差异,方言用来处理这些差异。
2.2.3 使用hibernate反向工程
1, 生成pojo对象,即实体类。
2, 生成映射文件。ORM,就是实体类到表的映射,这个映射在映射文件中配置。
3, 修改主配置文件,让主配置文件指向映射文件。
<hibernate-mapping> <class name="com.xaygc.User" table="[user]" schema="dbo" catalog="testHibernate"> <id name="myId" type="java.lang.Integer"> <column name="id" /> <generator class="native" /> </id> <property name="userName" type="java.lang.String"> <column name="[name]" length="20" not-null="true" /> </property> <property name="age" type="java.lang.Integer"> <column name="age" not-null="true" /> </property> <property name="tel" type="java.lang.String"> <column name="tel" length="20" /> </property> <property name="address" type="java.lang.String"> <column name="address" length="20" /> </property> <property name="pwd" type="java.lang.String"> <column name="pwd" length="10" not-null="true" /> </property> </class> </hibernate-mapping> |
1, id表示主键。
2, property都是实体的属性。
3, column指的表中的字段。
强调:因为表明table用于列明name为sqlserver数据库的关键字,所以两边要加[].
2.2.4 编写代码
//1 加载配置文件 //1 加载配置文件 Configuration config = new Configuration().configure(); //2 生成SessionFactory DriverManger SessionFactory sf = config.buildSessionFactory(); //3生成session Session session = sf.openSession(); //4 开启事务 Transaction t = session.beginTransaction(); //5 进行持久化操作 User user = new User("zhangsan", 12, "123456", "xian", "meiyou"); try { session.save(user); //6 提交或者回滚事务 t.commit(); } catch (Exception e) { e.printStackTrace(); t.rollback(); } finally { //7关闭资源 session.close(); sf.close(); } |
各种框架是那么优雅的实现了ORM思想。但问题来了,以前开发使用的是hibernate框架,新项目要使用OJB,语法一样吗?使用方法一样吗?API一样吗?答案显然是否定的。那要使用的话只有重新学习,不错,因为您有hibernate基础,学习起OJB一定速度神快,那过些天项目要使用JDO呢?是不是得从新学习?天哪,程序员的苦日子什么时候才能结束。
这时我们想到,可不可以向JDBC那样定义一个ORM规范,让所有的框架去实现这个规范,这样我们只要学习了这个ORM规范就可以使用所有框架了。OK,SUN做了一件让人高兴的事情,制定了这样的规范,并且命名为JPA。
2.2.5
JPA规范
下面我们重点介绍JPA规范。
2.2.5.1
JPA是什么
JPA的全称是Java Persistence API,它通过注解或XML描述的方式完成对象关系映射。
同时,JPA就是一个ORM规范,具体点说就是一些类(很少)和接口(很多)的集合。
JPA的实现:
? Oracle提供Toplink实现
? Bea提供KODO实现
? JBoss提供Hibernate EntityManager实现
? 其他厂商的实现…Apache OpenJPA
图1-4:JPA规范部分实现
你可以暂时把JPA理解为是一些接口的集合,各个框架就实现了这些接口,所以只要掌握了JPA规范就可以使用所有实现框架了,免去了重复学习的苦恼。
这里我们也能了解SUN引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用的对象持久化的开发工作;其二,Sun希望对ORM技术整合,实现天下归一,其实旺旺认为这点原因居多,并且很成功,从众多实现框架的响应可以看出。
同时,Sun这次吸取了之前EJB规范惨痛失败的经历,试图充分吸收现有ORM框架的优点,得到了一个易于使用、伸缩性强的ORM规范。如hibernate的创始者Gavin King也是JPA(EJB)规范的制定者,或者事实上的领导者,所以当您学习完Hibernate后学习JPA将非常轻松。
2.2.5.2
JPA规范包含内容
JPA的总体思想和现有Hibernate、TopLink,JDO等ORM框架大体一致。总的来说,JPA包括以下3方面的技术:
ORM映射元数据,JPA支持XML和JDK 5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
JPA的API,用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。
查询语言,JPAQL,这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
2.2.5.3
JPA与EJB关系
JPA与EJB的关系:JPA由EJB3.0软件专家组开发,作为JSR-220实现的一部分,所以JPA属于EJB,是它的一部分,从这个角度说我们学习JPA,相当于学习了部分EJB。但它不仅限于EJB3.0,能够脱离容器独立运行,你可以在Web应用、甚至桌面应用中使用。这里的容器指可以运行EJB的中间件服务器如Jboss等,关于EJB,旺旺老师后期会有专门的课程讲解。
2.2.5.4
JPA在应用程序中的位置
图1-5:JPA在应用程序中的位置
2.2.5.5 JPA与JDBC的关系
JPA与JDBC都是规范,JDBC是用Java应用程序连接数据库执行SQL语句的规范,而JPA是个ORM规范,ORM规范的实现者一定使用了JDBC规范。
2.3
JPA开发流程(MyEclipse 8.6+MySQL)
2.3.1
准备工作
JPA前期的准备工作包括创建数据库表(使用MySQL数据库),使用MyEclipse工具的DBBrowser创建连接以及创建Java项目,这里旺旺不再复述。
CREATE DATABASE jpaTest; USE jpaTest; CREATE TABLE student( stuId INT AUTO_INCREMENT PRIMARY KEY, stuName VARCHAR(20) NOT NULL, stuAge INT NOT NULL, context VARCHAR(50) NULL ); SELECT * FROM student |
2.3.2
添加JPA支持
图1-6:项目添加JPA支持
1.4.2.1选择框架类型
图1-7:选择支持框架
大家看到在MyEclispe8.5中添加的支持JPA的框架有Toplink,Hibernate与OpengJPA,还有MyEclispe组织自己开发的EclipseLink。这里我们暂时选择Hibernate的3.3版本。
1.4.2.2持久化单元选择
下图中的配置信息将影响到将来生成的JPA主配置文件。特别是Persistence unit name(持久化单元名称),我们在项目代码中要使用。
至于连接信息与数据库名称旺旺就不说了。
图1-8:持久化单元选择
1.4.2.3结果
图1-9:项目结构图
? Hibernate3.3 Core 是运行hibernate必备的东东
? Hibernate3.3 Core Annotation 则是hibernate对JPA支持的jar包
? Hibernate3.3 Advanced 主要是C3PO数据库连接池以及JBoss缓存等,暂时可以不用。
? 与单独使用Hibernate类似,JPA生成一个主配置文件,默认在ME-TA目录下,内容:
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="wangwangJpaPU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name = "hibernate.connection.driver_class" value = "com.mysql.jdbc.Driver"/> <property name = "hibernate.connection.url" value = "jdbc:mysql://localhost:3306"/> <property name = "hibernate.connection.username" value = "root"/> <property name = "hibernate.connection.password" value = "root"/> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence> |
配置文件不详细说了,还是希望大家注意上面方框内标红的持久化单元名称,后期使用。<property name="hibernate.show_sql" value="true" />为是否显示生成的SQL语句。
2.3.3
添加POJO类
JPA提供了两种方式配置持久化对象:
? POJO类加映射文件的方式
? 使用注解标识的持久化类,我们成为映射类。这是我们以后使用的重点。
1.4.3.1MyEclipse反转工具生成
这里我们使用MyEclipse工具提供的反转工具生成。
反转工程做两件事情:1,生成映射类。2,映射类在主配置文件中配置
图1-10:生成POJO反转工程
默认的实体名为表明,属性名为字段名,同时提供修改。
图1-10:生成POJO修改实体属性名
1.4.3.2映射类详解
package com.wangwang.dao.pojo; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import static javax.persistence.GenerationType.IDENTITY; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="student", catalog="jpatest") public class Student implements java.io.Serializable { @Id @GeneratedValue(strategy=IDENTITY) @Column(name="stuId", unique=true, nullable=false) private Integer id; @Column(name="stuName", nullable=false, length=20) private String name; @Column(name="stuAge", nullable=false) private Integer age; @Column(name="context", length=50) private String context; /* * get set 访问器省略 */ |
@Entity:将领域对象标注为一个实体,表示需要保存到数据库中。
@Table:默认情况下类名即为表名,通过name属性显式指定表名,通过catalog设置数据库名,如果使用的是SQLServer,还有schema角色属性,我们使用一般为dbo。
@Id:对应的属性是表的主键,一个实体只有一个
@GeneratedValue:主键的产生策略,通过strategy属性指定。默认情况下,JPA自动选择一个最适合底层数据库的主键生成策略,如SqlServer对应identity,MySql对应auto increment。在javax.persistence.GenerationType中定义了以下几种可供选择的策略:
? IDENTITY:表自增键字段,Oracle不支持这种方式;
? AUTO: JPA自动选择合适的策略,是默认选项;
? SEQUENCE:通过序列产生主键,通过@SequenceGenerator注解指定序列名,MySql不支持这种方式;
? TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。暂不研究。
@Column(name = "stuName"):属性对应的表字段。我们并不需要指定表字段的类型,因为JPA会根据反射从实体属性中获取类型;如果是字符串类型,我们可以指定字段长度,以便可以自动生成DDL语句。
@Temporal(TemporalType.DATE):如果属性是时间类型,因为数据表对时间类型有更严格的划分,所以必须指定具体时间类型。在javax.persistence.TemporalType枚举中定义了3种时间类型:
? DATE :等于java.sql.Date
? TIME :等于java.sql.Time
? TIMESTAMP :等于java.sql.Timestamp
关于字段的属性配置,可以放在变量声明的上面,也可以放在get方法的上面,最好不要混合使用。
1.4.3.3JPA主配置文件配置映射类
<persistence-unit name="wangwangJpaPU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>com.wangwang.dao.pojo.Student</class> <properties> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> <!—省略代码--> </properties> </persistence-unit> |
1.4.3.4映射类状态
实体对象拥有以下4个状态,这些状态通过调用EntityManager接口方法发生迁移:
? 新建态:新创建的实体对象,尚未拥有持久化主键,没有和持久化上下文关联起来。
? 受控态:已经拥有持久化主键并和持久化上下文建立了联系;
? 游离态:拥有持久化主键,但尚未和持久化上下文建立联系;
? 删除态:拥有持久化主键,已经和持久化上下文建立联系,但已经被安排从数据库中删除。
1.4.3
编写JPA代码
DAO类代码:
package com.wangwang.dao.impl; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import com.wangwang.dao.IStudentDAO; import com.wangwang.dao.pojo.Student; public boolean save(Student stu) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("testJPAPU"); EntityManager em = emf.createEntityManager(); EntityTransaction et = em.getTransaction(); et.begin(); boolean isSave = true; try { em.persist(stu); et.commit(); } catch (Exception e) { e.printStackTrace(); et.rollback(); isSave = false; } finally { em.close(); emf.close(); } return isSave; } |
1.4.4 编写单元测试,运行
测试代码:
IStudentDAO studentDAO; @Before public void setUp() throws Exception { studentDAO = new StudentDAOJPAImpl(); } @After public void tearDown() throws Exception { studentDAO = null; } @Test public void testSave() { Student student = new Student("wangwang", 28, "娃是好娃,社会把娃海了"); Assert.assertEquals(studentDAO.save(student), true); } |
2.4 JPA组件
JavaEE 5.0中所定义的JPA接口个数并不多,它们位于javax.persistence和javax.persistence.spi两个包中。 javax.persistence包中大部分API都是注解类,除此之外还包括EntityManager、Query等持久化操作接口。而 javax.persistence.spi包中的4个API,是JPA的服务层接口。
2.4.1 PersistenceProvider组件
我们一直说JPA是一个持久化层规范,各个框架是实现,那如何编写实现呢?首先框架提供者必须实现jpa的javax.persistence.spi.PersistenceProvider接口(PersistenceProvider接口它定义了创建一个EntityManagerFactory实例的方法:)
如hibernate中的org.hibernate.ejb.HibernatePersistence就实现了该接口。
然后此类必须在主配置JPA主配置文件中声明,以便程序使用它。如以下是Hibernate的声明。
<persistence-unit name="wangwangJpaPU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>com.wangwang.dao.pojo.Student</class> <!—省略代码--> </persistence-unit> |
2.4.2 Persistence 组件
这时JPA规范中的唯一一个类,Persistence的功能是利用注册的具体PersistenceProvider创建实体工厂类EntityManagerFactory。
Persistence它更像一个PersistenceProvider管理者,或者说PersistenceProvider代理类,即根据系统注册使用的PersistenceProvider创建EntityManagerFactory,如上例中我们使用Persistence.createEntityManagerFactory()方法本质上是调用HibernatePersistence的createEntityManagerFactory方法产生EntityManagerFactory。
这里其实还有一个PersistenceUnitInfo接口,有了一个void addTransformer(ClassTransformer transformer)方法,通过该方式可以添加一个javax.persistence.spi.ClassTransformer,并通过 PersistenceProvider开放给容器,以便容器在实体类文件加载到JVM之前进行代码的增强,使元数据生效。JPA厂商负责提供 ClassTransformer接口的实现。
图1-10:JPA各组件关系
2.4.3 EntityManagerFactory组件
接口,负责创建EntityManager。
2.4.4 EntityManager组件
接口,实体对象由实体管理器进行管理,JPA使用javax.persistence.EntityManager代表实体管理器。实体管理器和持久化上下文关联,持久化上下文是一系列实体的管理环境,我们通过EntityManager和持久化上下文进行交互。
有两种类型的实体管理器:
容器型(JavaEE容器,中间件服务器,JBoss等):容器型的实体管理器由容器负责实体管理器之间的协作,在一个JTA事务中,一个实体管理器的持久化上下文的状态会自动广播到所有使用EntityManager的应用程序组件中。Java EE应用服务器提供的就是管理型的实体管理器;这个在后期讲解EJB时候再讲解使用。
应用程序型:实体管理器的生命周期由应用程序控制,通过EntityManagerFactory的createEntityManager创建EntityManager实例。现在我们使用使用的就是应用程序型。
下面是EntityManager的一些主要的接口方法:
void persist(Object entity)
新实体实例将转换为受控状态。这意谓着当persist ()方法所在的事务提交时,实体的数据将保存到数据库中。如果实体已经被持久化,那么调用persist()操作不会发生任何事情。如果对一个已经删除的实体调用persist()操作,删除态的实体又转变为受控态。如果对游离状的实体执行persist()操作,将抛出 IllegalArgumentException。
在一个实体上调用persist()操作,将广播到和实体关联的实体上,执行相应的级联持久化操作;
void remove(Object entity)
通过调用remove()方法删除一个受控的实体。如果实体声明为级联删除(cascade=REMOVE 或者cascade=ALL ),被关联的实体也会被删除。在一个新建状态的实体上调用remove()操作,将被忽略。如果在游离实体上调用remove()操作,将抛出 IllegalArgumentException,相关的事务将回滚。如果在已经删除的实体上执行remove()操作,也会被忽略;
void flush()
将受控态的实体数据同步到数据库中;
T merge(T entity)
将一个游离态的实体持久化到数据库中,并转换为受控态的实体;
T find(Class entityClass, Object primaryKey)
以主键查询实体对象,这里使用的是即时加载。entityClass是实体的类,primaryKey是主键值,如以下的代码查询Student实体:
Student t = em.find(Student.class,1);
Query createQuery(String qlString)
创建查询接口对象
2.5 HibernateAPI运行
主配置文件
<session-factory> <property name="dialect"> org.hibernate.dialect.SQLServerDialect </property> <property name="connection.url"> jdbc:sqlserver://localhost:1433 </property> <property name="connection.username">sa</property> <property name="connection.driver_class"> com.microsoft.sqlserver.jdbc.SQLServerDriver </property> <property name="myeclipse.connection.profile">mssql</property> <property name="show_sql">true</property> <mapping class="com.wangwang.dao.pojo.Student" /> <mapping class="com.wangwang.Teacher" /> </session-factory> |
运行类:
public static void main(String[] args) { //Configuration config = new Configuration().configure(); AnnotationConfiguration config = new AnnotationConfiguration().configure(); SessionFactory sf = config.buildSessionFactory(); Session se = sf.openSession(); Student stu = (Student) se.get(Student.class, 1); System.out.println(stu.getBtd()); se.close(); sf.close(); } |
第二章:关联映射
2.1四种映射关系
2.2单双向的区别
单向是只在一方有另一方的引用。谁在前,谁就有对方应用。
一对多,一方有多方的Set集合,多方有一方的实体引用。
2.3 双向一对多关联
2.3.1一的一方
@OneToMany(cascade = CascadeType.ALL, fetch = CascadeType.LAZY, mappedBy = "men") public Set<Women> getWomens() { return this.womens; } |
@OneToMany 表示一对多关联关系的注解,括号内全是他的属性。
cascade={CascadeType.PERSIST,CascadeType.REMOVE} 设置保存和删除级联,如果只有一种方式,不需要{}。
fetch=FetchType.LAZY 即时加载或延迟加载
mappedBy = "men" 定义类之间的双向关系,如果是单向,不需要设定。
targetEntity=Order.class 指定关联的实体类,通常情况下不需要指定。系统根据属性自动设置。
2.3.2多的一方
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "menId", nullable = false) public Men getMen() { return this.men; } |
@ManyToOne 表示多对一的关联关系的注解,括号内全是他的属性。
cascade={CascadeType.PERSIST} 设置保存级联。
@JoinColumn 设置Women表的外键。name="menId"外键的名称。
2.4 双对多关联
一方编写:Course
@ManyToMany(fetch= FetchType.LAZY, mappedBy="courses")
mappedBy为对方实体类中自己类型的set集合。
另一方编写:Student
@JoinTable( catalog="ManyToMany", schema="dbo", name="student_course_link",joinColumns={@JoinColumn(name="sid")},inverseJoinColumns={@JoinColumn(name="cid")} )
Catalog 数据库名
Schema角色名
Name 中间表名
joinColumns 自己与中间表对应的外键列 sid
inverseJoinColumns 对方与中间表对应的外键列 cid
{}表示可以为数组等多个
第三章:JPA查询
JPAQL
普通查询命
支持模糊查询
支持聚合函数
getSingleResult返回的是单个结果,包括一个普通的POJO
名参数查询
String jpaQL = "select t from Student t where t.name like :name and t.age>= :age"; Query q = em.createQuery(jpaQL); q.setParameter("name", "wang%"); q.setParameter("age", 28); System.out.println(q.getResultList().size()); |
分页查询
String jpaQL = "select t from Student t"; Query q = em.createQuery(jpaQL); q.setFirstResult(6);//(nowPage-1)pageSize q.setMaxResults(3); List<Student> stus = q.getResultList(); |
Query执行save,update,delete
String jpaQL = "delete from Student t where t.id = 15"; em.getTransaction().begin(); Query q = em.createQuery(jpaQL); q.executeUpdate(); em.getTransaction().commit(); |
em.createNativeQuery("sql")支持原生态sql。
第四章:与Spring整合
Spring框架提供的IOC与AOP可以更好高效的编写应用程序,同时,从spring2.0开始提供了对JPA的整合支持
4.1得到EntiteManagerFactoryBean
JPA中的重要组件为EntiteManagerFactoryBean,Spring提供了以下三种方式配置和获取EntiteManagerFactoryBean。
4.1.1 LocalEntityManagerFactoryBean
LocalEntityManagerFactoryBean负责创建一个适合于仅使用JPA进行数据访问的环境的 EntityManager。 Factory bean将使用JPA PersistenceProvider 类的自动检测机制,只需要指定persistence unit名称:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="springAndJpaPU" /> </bean> |
这种JPA部署方式最为简单,但却最受限制。例如,不能连接到现有的DataSource,不能设置JPA主配置文件位置,并且不支持全局事务。这种方法实际上只适用于独立的应用程序和测试环境。
4.1.2 LocalContainerEntityManagerFactoryBean
LocalContainerEntityManagerFactoryBean 提供了对JPA EntityManagerFactory 的全面控制,非常适合那种需要细粒度定制的环境。LocalContainerEntityManagerFactoryBean 将基于 persistence.xml 文件创建 PersistenceUnitInfo 类,并提供 dataSourceLookup 策略和 loadTimeWeaver。 因此它可以在JNDI之外的用户定义的数据源之上工作,并控制织入流程。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"> </property> <property name="url" value="jdbc:sqlserver://127.0.0.1:1433"></property> <property name="username" value="sa"></property> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="springAndJpaPU" /> <property name="dataSource" ref="dataSource"></property> <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"></property> </bean> |
注意,这个选项可能与Java EE5服务器内建的JPA功能相冲突。因此,当运行在完全JavaEE5环境中时,要考虑从JNDI获取EntityManagerFactory
。另一种可以替代的方法是,在LocalContainerEntityManagerFactoryBean
定义中通过 “persistenceXmlLocation”指定相关位置, 例如“META-INF/my-persistence.xml”,并且只将包含该名称的描述符放在应用程序包文件中。因为Java
EE 5服务器将只查找默认的META-INF/persistence.xml
文件,它会忽略这种定制的持久化单元,因而避免与前面 Spring 驱动的JPA配置冲突。
多持久化单元:
<property name="persistenceXmlLocation"> <list> <value>org/springframework/orm/jpa/domain/persistence-multi.xml</value> <value>classpath:/my/package/**/custom-persistence.xml</value> <value>classpath*:META-INF/persistence.xml</value> </list> </property> |
4.1.3 JDNI方式
从JNDI获取 EntityManagerFactory (例如在Java EE 5环境中),仅通过修改XML配置即可实现:
<beans>
<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/myPersistenceUnit"/>
</beans>
在标准的Java EE 5启动过程中,Java EE服务器自动检测持久化单元(例如应用程序文件包中的META-INF/persistence.xml) ,以及Java EE部署描述符中定义给那些持久化单元命名上下文位置的环境的persistence-unit-ref项(例如web.xml)。
在这种情况下,整个持久化单元部署,包括持久化类的织入(字码码转换)都取决于Java EE服务器。 JDBC DataSource 通过在META-INF/persistence.xml 文件中的JNDI位置进行定义;EntityManager事务与服务器的JTA子系统整合。Spring仅仅用获得的EntityManagerFactory, 通过依赖注入将它传递给应用程序对象,并为它管理事务(一般通过JtaTransactionManager)。
注意,如果在同一个应用程序中使用了多个持久化单元,JNDI获取的这种持久化单元的bean名称 应该与应用程序用来引用它们的持久化单元名称相符(例如@PersistenceUnit和 @PersistenceContext注解)。
在部署到Java EE 5服务器时使用该方法。关于如何将自定义JPA提供者部署到服务器,以及允许使用服务器提供的缺省提供者之外的JPA提供者,请查看服务器文档的相关说明。
4.2 使用JpaDaoSupport
每个基于JPA的DAO将通过IoC接收一个 EntityManagerFactory 实例。这样的DAO可以通过EntityManagerFactory来操作原生JPA的API进行数据访问,也可以直接使用Spring的JpaTemplate:
package com.wangwang.dao.impl; import org.springframework.context.ApplicationContext; import org.springframework.orm.jpa.JpaCallback; import org.springframework.orm.jpa.support.JpaDaoSupport; public class BankDAO extends JpaDaoSupport implements IBankDAO { @SuppressWarnings("unchecked") public List<Bank> findByProperty(String propertyName, final Object value) { logger.info("finding Bank instance with property: " + propertyName + ", value: " + value); try { final String queryString = "select model from Bank model where model." + propertyName + "= :propertyValue"; return getJpaTemplate().executeFind(new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException { Query query = em.createQuery(queryString); query.setParameter("propertyValue", value); return query.getResultList(); } }); } catch (RuntimeException re) { logger.error("find by property name failed", re); throw re; } } } |
Spring还提供了一个方便的 JpaDaoSupport 基类,提供了setEntityManagerFactory方法以及getJpaTemplate()方法供子类调用。
JpaTemplate 将确保 EntityManager 正确的打开和关闭,并且能够自动地参与到事务中去。除此之外,JpaTemplate 能够恰当地处理异常,确保资源的及时清理以及必要时的事务回滚。
Template实例不仅是线程安全的,而且它是可重用的,因而它能够作为实例变量被一个类持有。JpaTemplate 提供了简单的诸如find、load、merge等操作的快捷函数来替代默认的回调实现。
JpaCallback实现允许所有类型的JPA数据访问。
public interface JpaCallback { //此方法的参数为EntityManager Object doInJpa(EntityManager em) throws PersistenceException; } |
具体使用:
package com.wangwang.dao.impl; import org.springframework.context.ApplicationContext; import org.springframework.orm.jpa.JpaCallback; import org.springframework.orm.jpa.support.JpaDaoSupport; public class BankDAO extends JpaDaoSupport implements IBankDAO { @SuppressWarnings("unchecked") public List<Bank> findByProperty(String propertyName, final Object value) { logger.info("finding Bank instance with property: " + propertyName + ", value: " + value); try { final String queryString = "select model from Bank model where model." + propertyName + "= :propertyValue"; return getJpaTemplate().executeFind(new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException { Query query = em.createQuery(queryString); query.setParameter("propertyValue", value); return query.getResultList(); } }); } catch (RuntimeException re) { logger.error("find by property name failed", re); throw re; } } } |
4.3 原生JPA实现DAO
除了直接使用Spring的 JpaTemplate之外,也可以使用原生JPA的API来实现基于Spring的DAO,此时你需要自行明确地处理EntityManager。我们通过JPA@PersistenceContext注解 获取的一个“共享EntityManager”引用。
public class BankDAO extends JpaDaoSupport implements IBankDAO { @PersistenceContext private EntityManager em; } |
以上需要<context:annotation-config />的支持
4.4 添加Services与完成自动装配
参考旺旺老师《Spring高级编程》
4.5 JPA事务
4.5.1事务类型
EntityManager对象的事务管理方式有两种,分别为JTA和 RESOURCE_LOCAL,即Java Transaction API方法和本地的事务管理。
JPA 中的事务类型通过persistence.xml文件中的“transaction-type”元素配置。
<?xml version="1.0" encoding="UTF-8"?> <persistence> <persistence-unit name="wangwangJpaPU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> </persistence-unit> </persistence> |
不同EntityManager组件在不同运行环境中的事务支持是不同的。
JavaEE环境 |
JavaSE环境 |
||
EJB容器 |
Web容器 |
||
应用程序型EntiteManager |
JTA,RESOURCE_LOCAL |
JTA,RESOURCE_LOCAL |
LOCAL |
容器型EntiteManager |
JTA |
不支持 |
不支持 |
其中两种情况下最为简单,一种是容器托管的EntityManager只能运行在EJB容器中,只能采用JTA的方式管理事务;另一种是J2SE环境下,只能使用应用托管的 EntityManager并且只能采用RESOURCE_LOCAL的方式管理事务。
JTA:JTA事务(Java Transaction API)是JavaEE规范中有关事务的标准。它是容器级别的事务,只能运行在JavaEE服务器中。它的最大优势是可以支持分布式的事务,如果系统采用的是分布式的数据库,那么只能选择JTA管理EntityManager事务。
RESOURCE_LOCAL:RESOURCE_LOCAL事务数据库本地的事务。它是数据库级别的事务,只能针对一种数据库,不支持分布式的事务。对于中小型的应用,可以采用RESOURCE_LOCAL管理 EntityManager事务。
注意:采用RESOURCE_LOCAL管理事务时,要保证数据库支持事务。例如使用MySQL时,需要设置数据库的引擎类型为 “InnoDB”,而“MyISAM”类型是不支持事务的。
4.5.1事务代码
@Repository ("bankDAO") public class BankDAO extends JpaDaoSupport implements IBankDAO { @Resource(name = "entityManagerFactory") public void setEMF(EntityManagerFactory emf) { super.setEntityManagerFactory(emf); } } |
事务类型与含义:
类型 |
含义 |
required
|
如果上下文中已经有事物,则使用当前事务,若无,则启动一个新事物
|
requiresNew
|
如果上下文已经有事务,挂牵它,创建新的事务运行本业务
|
mandatory
|
必须加入现有事务中,如无事务,抛出异常
|
supports
|
如果上下文中已经有事物,则使用当前事务,若无,则不启动事物
|
notSupported
|
不管上下文是否有事物,从不启动一个事物
|
never
|
不管上下文是否有事物,从不启动一个事物。如果有事物,抛出异常
|
4.6 整合过程总步骤
1. 添加JPA支持,配置控制台打印sql脚本
2. 添加Spring支持,添加四个支持包
3. Spring配置文件中添加DS
4. entityManagerFactory使用LocalContainerEntityManagerFactoryBean,配置entityManagerFactory
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="persistenceUnitName" value="testzhenghePU" /> <property name="persistenceXmlLocation" value="classpath:/META-INF/persistence.xml"></property> </bean> |
5. 注释掉persistence.xml的DS设置
6. 生成POJO,如果使用的SQLServer数据库,设置主键生成方式
7. 给项目添加自动装配支持
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:annotation-config/> <context:component-scan base-package="com.wangwang"></context:component-scan> </beans> |
8. 生成DAO,并配置DAO,使用自动装配
@Repository ("bankDAO") public class BankDAO extends JpaDaoSupport implements IBankDAO { @Resource(name = "entityManagerFactory") public void setEMF(EntityManagerFactory emf) { super.setEntityManagerFactory(emf); } } |
9. 创建并配置services
@Service("zhuanzhangServices") public class ZhuanzhangServicesImpl implements IZhuangzhangServices { @Resource(name = "bankDAO") private IBankDAO bankDAO; } |
10. 在Iservices中配置事务
@Transactional public interface IZhuangzhangServices { @Transactional(propagation = Propagation.REQUIRED, readOnly = true) public boolean zhuanzhang(int inId, int outId, int money); } |
11. 添加单元测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"/applicationContext.xml"}) public class ZhuanzhangServicesImplTest { @Resource(name = "zhuanzhangServices") private IZhuangzhangServices zhuangzhangServices; @Test public void testZhuanzhang() { this.zhuangzhangServices.zhuanzhang(2, 1, 100); } } |
版权声明:欢迎转载,希望在你转载的同时,添加原文地址,谢谢配合