1、JPA介绍以及初衷:JPA属于Hibernate的前身ORM框架,是一个更高层次的抽象,Hibernate是JPA的一个具体实现,对JPA进行了扩展和延伸,结合Spring,JPA在应用上面具有很好的实用性,因此,学好JPA对学习ORM框架具有很重要的意义。自己在学习JPA过程中遇到的问题,百思不得其解,最后发现都是一些小问题导致的,记录这些问题的解决方案以及总结问题,避免以后犯同样的错误其实就是我写这边博客的初衷。
2、项目的结构如图:
3、web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
4、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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.edward.springdata" />
<!--配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="sa"/>
<property name="password" value="123"></property>
<property name="driverClass" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"></property>
<property name="jdbcUrl" value="jdbc:sqlserver://localhost;databaseName=Sample"></property>
</bean>
<!-- 2. 配置 JPA 的 EntityManagerFactory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
</property>
<property name="packagesToScan" value="com.edward.springdata"></property>
<property name="jpaProperties">
<props>
<!-- 二级缓存相关 -->
<!--<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>-->
<!--
<prop key="net.sf.ehcache.configurationResourceName">ehcache-hibernate.xml</prop>
-->
<!-- 生成的数据表的列的映射策略 -->
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<!-- hibernate 基本属性 -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- 3. 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!-- 4. 配置支持注解的事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 5. 配置 SpringData -->
<!-- 加入 jpa 的命名空间 -->
<!-- base-package: 扫描 Repository Bean 所在的 package -->
<jpa:repositories base-package="com.edward.springdata"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
</beans>
5、一对一双向映射:Category中有Customer属性,Customer中有Category属性
Category类:@OneToOne(mappedBy=“category”) :不作为一对一关系处理的一方,category为Customer中定义的Category属性:category
package com.edward.springdata.bean;
import javax.persistence.*;
@Entity
@Table(name="Category")
public class Category {
private String cid;
private String cname;
private Customer customer;
@OneToOne(mappedBy = "category")
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
@Id
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
@Column
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
@Override
public String toString() {
return "Category{" +
"cid='" + cid + '\'' +
", cname='" + cname + '\'' +
'}';
}
}
Customer类:@OneToOne标注一对一映射,@JoinColumn(name=“cid”,unique=true),必须加上unique=true,cid是对应关联表Category表的主键
package com.edward.springdata.bean;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.util.Date;
import java.util.Set;
//单向一对多 Customer中有Set<Orders>属性 而Orders中没有Customer属性
@Entity
@Table(name = "Customer")
public class Customer {
private String uid;
private String username;
private String password;
private String name;
private String email;
private String telephone;
private Date birthday;
private String sex;
private Integer state;
private String code;
private Set<Orders> orders;
private Category category;
@JoinColumn(name="cid",unique = true)
@OneToOne
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
@Id
@GenericGenerator(name = "user-uuid", strategy = "uuid2")
@GeneratedValue(generator = "user-uuid")
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
@Column(name="username")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@JoinColumn(name="uid")
@OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.REMOVE)
//@OneToMany
public Set<Orders> getOrders() {
return orders;
}
public void setOrders(Set<Orders> orders) {
this.orders = orders;
}
}
6、多对一单向映射:Orders类中有Customer属性,Customer类中没有Set属性
Orders类:@ManyToOne标注多对一的映射,fetch=FetchType.EAGER属于主动加载,相对应是FetchType.LAZY属于懒加载,@JoinColumn(name=“uid”),uid属性对应关联表的主键,在当前表默认为对应的外键
package com.edward.springdata.bean;
import org.hibernate.FetchMode;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.data.web.JsonPath;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import java.util.Date;
//单向多对一 Orders中有Customer属性 而Customer中没有Orders属性
@Entity
@Table(name = "Orders")
public class Orders {
private String oid;
private String code;
private Date ordertime;
private Double total;
private Integer state;
private String address;
private String name;
private String telephone;
private Customer customer;
@Id
@GenericGenerator(name="order-uuid",strategy = "uuid2")
@GeneratedValue(generator = "order-uuid")
public String getOid() {
return oid;
}
public void setOid(String oid) {
this.oid = oid;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Temporal(TemporalType.DATE)
public Date getOrdertime() {
return ordertime;
}
public void setOrdertime(Date ordertime) {
this.ordertime = ordertime;
}
public Double getTotal() {
return total;
}
public void setTotal(Double total) {
this.total = total;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name="telephone")
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
@JoinColumn(name="uid") //这个name对应关联表的主键 不要写错
@ManyToOne(fetch = FetchType.EAGER)
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
7、一对多关联映射:Customer中有Set属性,Orders中没有Customer属性
Customer类中@OneToMany标注一对多映射,cascade表示级联策略,CascadeType.REMOVE表示级联删除,@JoinColumn(name=“uid”),其中uid是当前表Customer的主键,并且在Orders表中将作为外键存在
8、测试类:
JpaTest类:通过ClassPathXmlApplicationContext加载ApplicationContext对象,注意由于我们的ApplicationContext.xml不是在类路径下面构建的,所以这里使用该文件的绝对路径进行加载;然后通过getBean方法获取CustomerService对象;
package com.edward.springdata;
import com.edward.springdata.bean.Customer;
import com.edward.springdata.service.CustomerService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JpaTest {
private ApplicationContext ctx=null;
private CustomerService cs=null;
{
ctx = new ClassPathXmlApplicationContext("file:E:\\JAVA\\ideacase\\springdatajpa\\src\\main\\webapp\\WEB-INF\\applicationContext.xml");
cs=ctx.getBean(CustomerService.class);
}
@Test
public void testSave(){
// Customer p1=new Customer();
// p1.setEmail("a@123.com");
// p1.setName("aa");
//
// Customer p2=new Customer();
// p2.setEmail("b@123.com");
// p2.setName("bb");
// System.out.println(cs.getClass().getName());
// cs.save(p1,p2);
}
@Test
public void test1(){
cs.findByname("DA4FD78A-272C-4E0D-AF78-67E378111382");
}
}
9、依赖注入:首先在ApplicationContext.xml配置文件中通过<context:component-scan base-package=“com.edward.springdata” />将对应包下面的类进行了扫描。
CustomerService类:通过注解@Service进行组件的注册,@Autowired标注进行注入CustomerDao。
package com.edward.springdata.service;
import com.edward.springdata.bean.Customer;
import com.edward.springdata.dao.CustomerDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CustomerService {
@Autowired
private CustomerDao dao;
@Transactional
public void save(Customer p1,Customer p2){
dao.save(p1);
//int i=10/0;
dao.save(p2);
}
public void findByname(String name){
dao.findByname(name);
}
}
CustomerDao类:@Repository标注仓储
package com.edward.springdata.dao;
import com.edward.springdata.bean.Customer;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.HashMap;
import java.util.Map;
@Repository
public class CustomerDao {
@PersistenceContext
private EntityManager entityManager;
public void save(Customer p){
entityManager.persist(p);
}
public void findByname(String id){
Customer customer=new Customer();
customer=entityManager.find(Customer.class,id);
System.out.println(customer);
}
}
总结:
1)、注解最好是都打在getter方法上面,不要有的地方打在变量上面,有些地方打在getter方法上面;
2)、不要尝试在方法里面重载toString()的方法,这样会影响延迟加载的策略(报错),影响使用;
3)、其实不管是那种映射,本质上和数据库表的设计是一样的,我们可以基于数据表的设计来构建我们的映射关系,从而实现数据表的自动生成;
4)、JPA能做的,最终就是要实现我们对数据库的操作。