JPA学习笔记
JPA
一、基于 Hibernate 数据库持久化操作及 JPA 初始配置
需要导入的基础包
<denpendency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</denpendency>
<denpendecy>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.32.Final</version>
</denpendecy>
<!-- lombok,演示仅用作生成setter&getter,可以手动生成 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
声明实体的例子:
@Data
@Entity
@Table(name = "table_name")
public class Object {
/**
* @Id:用于声明主键
* @GeneratedValue:配置主键的生成策略
* strategy
* GenerationType.IDENTITY : 自增,MySQL
* * 底层数据库必须支持自动增长(对id自增)
* GenerationType.SEQUENCE : 序列,Oracle
* * 底层数据库必须支持序列
* GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
* GenerationType.AUTO : 由程序自动地帮我们选择主键生成策略
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private id;
@Column(name = "other_properties")
private String otherProperties;
@Transient
private String unsavedProperties;
...
}
@Data:
给实体类加get/set/toString/EqualsAndHashCode方法,是lombok的注解
@Transient:
如果不想将该属性保存到数据库,可以使用这个注释
resources/hibernate.cfg.xml
相关配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置数据库连接信息 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/springdata_jpa?table?characterEncoding=UTF-8</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</ property>
<!-- 是否在日志中记录sql,默认为false -->
<property name="show_sql">true</property>
<!-- 是否格式化sql,默认为false -->
<property name="format_sql">update</property>
<!-- 配置方言:选择数据库类型 -->
<property name="dialect">org.hibernate.dialect.MYSQL5InnoDBDiaclect</property>
<!-- 映射 -->
<mapping class="Object"></mapping>
</session-factory>
</hibernate-configuration>
数据访问层一个简单的例子:
private SessionFactory sf;
@Before
public void init(){
StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure("/hibernate.cfg.xml").build;
sf = new MetaDataSources(registry).buildMetadata().buildSessionFactory();
}
@Test
public void Test(){
try(Session session = sf.openSession()){
Transcation transcation = session.beginTransaction;
Object object = new Object();
object.setOtherProperties("其他属性");
session.save(object);
transcation.commit();
}
}
插入和更新使用的语句都是
save()
(或saveOrUpdate()
)它们根据数据是否有id来判断是insert
还是update
删除和查询分别使用的方法是
remove()
、find(Object.class)
/load(Object.class)
使用hql
的例子:
@Test
public void Test(){
try(Session session = sf.openSession()){
Transcation transcation = session.beginTransaction;
String hql = " FROM Object";
List<Object> result = session.createQuery(hql, Object.class).getResultList();
transcation.commit();
}
}
二、JPA 对象的四种状态
临时状态:刚创建出来,没有与entityManager发生关系,没有被持久化
持久状态:已被持久化,处于entityManager,等同于已经存在在数据库中的数据
删除状态:提交remove方法后,事务提交前
游离状态:commit之后实体的状态,无论怎么修改都不对数据库造成影响
三、基于 JPA 数据库持久化操作
依赖导入配置
<dependency>
<groupId>org.apache.openjpa</groupId>
<artifactId>openjpa-all</artifactId>
<version>3.2.0</version>
</dependency>
创建META-INF/persistence.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/tas
k"
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/jpa" />
<property name="user" value="root" />
<property name="password" value="root"/>
</bean>
<!‐‐ 2.配置entityManagerFactory ‐‐>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="cn.itcast.entity" />
<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" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<!‐‐ 整合spring data jpa‐‐>
<jpa:repositories base‐package="cn.itcast.dao"
transaction‐manager‐ref="transactionManager"
entity‐manager‐factory‐ref="entityManagerFactory"></jpa:repositories>
<!‐‐ 3.事务管理器‐‐>
<!‐‐ JPA事务管理器 ‐‐>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!‐‐基于注解方式的事务,开启事务的注解驱动
如果基于注解的和xml的事务都配置了会以注解的优先
‐‐>
<tx:annotation‐driven transaction‐manager="transactionManager"></tx:annotation‐driven>
<context:component‐scan base‐package="cn.itcast"></context:component‐scan>
<!‐‐组装其它 配置文件‐‐>
</beans>
持久层操作:
EntityManagerFactory factory;
@Before
public void before(){
factory = Persistence.createEntityManagerFactory("xmlPersistenceUnitName");
}
@Test
public void test(){
EntityManager manager = factory.createEntityManager();
EntityTransaction transaction = manager.getTransaction();
}
JPA的entityManager的find方法与getReference方法的区别
find
返回指定的 OID 对应的实体类对象,如果这个实体存在于当前的持久化环境,则返回一个被缓存的对象;否则会创建一个新的
Entity
, 并加载数据库中相关信息;若 OID 不存在于数据库中,则返回一个 null。第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。getReference
与
find()
方法类似,不同的是:如果缓存中不存在指定的Entity
,EntityManager
会创建一个Entity
类的代理,但是不会立即加载数据库中的信息,只有第一次真正使用此Entity
的属性才加载,所以如果此 OID 在数据库不存在,getReference()
不会返回 null 值, 而是抛出EntityNotFoundException
find
方法类似于hibernate
中Session
的get
方法。
getReference
方法类似于hibernate
的Session
的load
方法。
find()
做了一次select
的操作,而getReference
并没有做有关数据库的操作,而是返回一个代理,这样它就减少了连接数据库和从数据库加载持久状态的开销。
使用jpql
的例子:
String jpql="UPDATE Customer set custName=:name where custId=:id";
manager.createQuery(jpql).setParameter("name","李四").setParameter("id",5L).executeUpdate().executeUpdate();
transaction.commit;
四、JPA 中映射关联关系
1.一对一映射
@OneToOne
执行一对一映射,比如一个学生对应一个档案,一个档案对应一个学生
@Entity
public class Student {
...
@OneToOne(fetch=LAZY)
@JoinColum(name="archive_id")
private Archive archive;
...
}
fetch=LAZY
可以实现该属性懒加载
@JoinColumn
为一对一映射设置连接列。 或者使用@PrimaryKeyJoinColum
设置主键连接列
@OneToOne(cascade=CascadeType.ALL,mappedBy="department")
主要是用来设置JPA的级联,他和@JoinColumn
是互斥的。JPA中的CascadeType.ALL并不等于**{CascadeType.PESIST,CascadeType.REMOVE,CascadeType.MERGE,CascadeType.REFRESH}**在Hibernate中调用
session.save()
orsession.update()
并不能触发 {CascadeType.PESIST,CascadeType.REMOVE,CascadeType.MERGE,CascadeType.REFRESH} 的级联操作,而能触发CascadeType.ALL的级联。如不希望用CascadeType.ALL,需要使用Hibernate自身对 cascade的注解 @Cascade(value=org.hibernate.annotations.CascadeType.SAVE_UPDATE)
mappedBy
被用来定义被拥有方的,被mappedBy定义的事被拥有方,拥有这个属性的是主导者,比如学生是档案的主导者,就可以让Student表做主导,通常还有一个option属性,比如Archive就应该为true代表一个档案是必须有人的,当是一个学生不一定有档案就可以设置option为false。
单向一对一映射
单向一对一映射更像是一个多对一的特殊版本,假如使用注解的形式进行配置会发现一个entity有one to one注解,一个没有,即一个表有外键一个没有外键。
2.一对多映射和多对一映射
一个学校可以有很多学生,很多学生也可以对应一个学校,这就反映了一对多和多对一映射:
@Entity
public class Student {
...
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumns({
@JoinColumn(name="SCHOOL_NAME", referencedColumnName="Name"),
@JoinColumn(name="SCHOOL_ID", referencedColumnName="ID")
})
private School school;
...
}
@Entity
public class School {
...
@OneToMany(targetEntity=Student.class, mappedBy="school")
@JoinTable(name=“INFO”,
joinCloums=@JoinColum(name="SCH_ID"),
inverseJoin=@JoinColum(name="STU_ID"))
@OrderBy("name ASC")
@MapKey(name="id")
private Set<Student> students = new HashSet<Student>;
...
}
这个例子里,School通过中间表EMP_INFO来关联Student
当多对一需要多个连接列时可以使用
@JoinColumns
@OrderBy
用于将一对多映射按属性设置排序。具体实现其实是在sql查询时加入order by
。或者通过@OrderColumn
进行排序,但可能会出现空数据的情况,比如:以id作为排序列,但是只有0,3,5这三个列,但是查询到的将会是0,Null,Null,NULL,3,NULL,5
@MapKey(name =“id")
设置为使用student id作为Student的映射键组。
3.多对多映射
一个普通公司可以雇佣好几个外包公司,一个外包公司也可以同时给多个公司提供外包服务,这就反映了多对多的映射关系:
@Entity
public class Subcontractor {
...
@ManyToMany
private Company company;
...
}
@Entity
@JoinTable(name="COM_EMP",
joinColumns=@JoinColumn(name="COM_ID"), inverseJoinColumns=@JoinColumn(name="SUB_ID"))
@AttributeOverride(name="sub_name",
column=@Column(name="SUB_NAME"))
public class Company {
...
@ManyToMany
private Subcontractor subcontractor;
...
}
这种映射往往需要两个中间表,如上所示,这里不多做讨论
@AttributeOverride
用于覆盖多对多映射
上一篇: 秒杀功能开发及管理后台
推荐阅读
-
(待续)Anaconda+Jupyter Notebook常用命令笔记
-
JAVASE 小白学习笔记 (12-3)Java中的常用类--StringBuffer类、StringBuilder类
-
从URL静态化与动态化之争谈搜索引擎优化技术(SEO)的学习
-
阿里技术专家23天纯手撸笔记,演绎最新“Kafka部署实战”,开源,限时白嫖
-
STM32学习笔记 —— 1.1 什么是寄存器(概念分析)
-
canvas游戏开发学习之五:运用样式与颜色(一)
-
JavaScript精粹读书笔记(2)
-
php学习笔记之 函数声明_php基础
-
javascript学习笔记(二十) 获得和设置元素的特性(属性)_基础知识
-
【原】《DIV+CSS商业案例与网页布局开发精讲》读书笔记(2)_html/css_WEB-ITnose