JPA 相关API (一)
【Query 接口下的常用API】
【API 测试类:Test_QueryAPI.java】
1 package org.zgf.jpa.entity; 2 3 import java.math.BigInteger; 4 import java.util.Calendar; 5 import java.util.Date; 6 import java.util.List; 7 8 import javax.persistence.Query; 9 import javax.persistence.TemporalType; 10 11 import org.junit.Test; 12 import org.zgf.jpa.enumer.Gender; 13 /** 14 * JPA 中sql 语句设置参数有两种形式,一种是命名参数,另一种是位置参数: 15 * 位置参数:执行效率高, 可读性差,可用于原生sql 和 JPQL 语句中, 格式《?+数字》,数字可任意指定 16 * 命名参数:执行效率低 ,可读性强,可用于原生sql 和 JPQL 语句中, 格式《:+ 名称》,名称自定义 17 * 18 * @Description: 测试Query接口的相关API 19 * @author zonggf 20 * @date 2015年11月4日-下午4:07:12 21 */ 22 public class Test_QueryAPI extends BasicJPATest{ 23 24 //2. Test API 1: 测试返回列表API 25 @Test 26 public void test_getResultList(){ 27 String jpql = "from PersonEntity"; 28 Query query = this.entityManager.createQuery(jpql); 29 List<PersonEntity> personEntityList = query.getResultList(); 30 for (PersonEntity personEntity : personEntityList) { 31 System.out.println(personEntity); 32 } 33 } 34 35 //2. Test API 2: 使用query.getSingleResult()方式时,必须保证查询的结果有且只有一个返回对象 36 @Test 37 public void test_getSingleResult(){ 38 String jpql = "from PersonEntity personEntity where personEntity.id = 1"; 39 Query query = this.entityManager.createQuery(jpql); 40 PersonEntity personEntity = (PersonEntity) query.getSingleResult(); 41 System.out.println(personEntity); 42 } 43 44 //2. Test API 2: 如果没有返回对象,则会抛出异常 NoResultException.class 45 @Test(expected=javax.persistence.NoResultException.class) 46 public void test_getSingleResult_Exception(){ 47 String jpql = "from PersonEntity personEntity where personEntity.id = 100"; 48 Query query = this.entityManager.createQuery(jpql); 49 PersonEntity personEntity = (PersonEntity) query.getSingleResult(); 50 System.out.println(personEntity); 51 } 52 53 //2. Test API 2: 如果有多个返回对象,则会抛出异常 NonUniqueResultException.class 54 @Test(expected=javax.persistence.NonUniqueResultException.class) 55 public void test_getSingleResult_Exception_(){ 56 String jpql = "from PersonEntity"; 57 Query query = this.entityManager.createQuery(jpql); 58 PersonEntity personEntity = (PersonEntity) query.getSingleResult(); 59 System.out.println(personEntity); 60 } 61 62 63 //3. Test API 3: 测试位置参数使用方法 64 @Test 65 public void test_setPositionParameter(){ 66 String jpql = "from PersonEntity personEntity where personEntity.id = ?1"; 67 Query query = this.entityManager.createQuery(jpql); 68 query.setParameter(1, 1); 69 PersonEntity personEntity = (PersonEntity) query.getSingleResult(); 70 System.out.println(personEntity); 71 72 } 73 74 //3. Test API 3: 测试位置参数使用方法,参数类型为java.util.Date 75 @Test 76 public void test_setPositionParameter_date(){ 77 String sql = "select count(*) from tb_person p where p.birthdayDate = ?2"; 78 Query query = this.entityManager.createNativeQuery(sql); 79 query.setParameter(2,new Date(), TemporalType.DATE); 80 //注意此处返回的是BigInteger 类型, 不是Int类型 81 BigInteger count = (BigInteger) query.getSingleResult(); 82 System.out.println("共有记录:" + count); 83 } 84 85 //3. Test API 3: 测试位置参数使用方法, 参数类型为 java.util.Calendar; 86 @Test 87 public void test_setPositionParameter_calender(){ 88 String sql = "select count(*) from tb_person p where p.birthdayDate = ?2"; 89 Query query = this.entityManager.createNativeQuery(sql); 90 query.setParameter(2,Calendar.getInstance(), TemporalType.DATE); 91 //注意此处返回的是BigInteger 类型, 不是Int类型 92 BigInteger count = (BigInteger) query.getSingleResult(); 93 System.out.println("共有记录:" + count); 94 } 95 96 //Test API 4: 测试命名参数:参数类型为普通参数 97 @Test 98 public void test_setNameParameter(){ 99 String jpql = "from PersonEntity personEntity where personEntity.id = :id"; 100 Query query = this.entityManager.createQuery(jpql); 101 query.setParameter("id", 1); 102 PersonEntity personEntity = (PersonEntity) query.getSingleResult(); 103 System.out.println(personEntity); 104 } 105 106 //Test API 4: 测试命名参数:参数类型为java.util.Date 107 @Test 108 public void test_setNameParameter_date(){ 109 String sql = "select count(*) from tb_person p where p.birthdayDate = :birthDate"; 110 Query query = this.entityManager.createNativeQuery(sql); 111 query.setParameter("birthDate",new Date(), TemporalType.DATE); 112 //注意此处返回的是BigInteger 类型, 不是Int类型 113 BigInteger count = (BigInteger) query.getSingleResult(); 114 System.out.println("共有记录:" + count); 115 } 116 117 //Test API 4: 测试命名参数:参数类型为java.util.Calendar 118 @Test 119 public void test_setNameParameter_calender(){ 120 String sql = "select count(*) from tb_person p where p.birthdayDate = :birthDate"; 121 Query query = this.entityManager.createNativeQuery(sql); 122 query.setParameter("birthDate",Calendar.getInstance(), TemporalType.DATE); 123 //注意此处返回的是BigInteger 类型, 不是Int类型 124 BigInteger count = (BigInteger) query.getSingleResult(); 125 System.out.println("共有记录:" + count); 126 } 127 128 //Test API 5: excuteUpdate 用于执行更新更新,删除语句或者DDL语句 129 @Test 130 public void test_excuteUpdate_clear(){ 131 String jpql = "delete PersonEntity"; 132 Query query = this.entityManager.createQuery(jpql); 133 int deleteCnt = query.executeUpdate(); 134 System.out.println("共删除记录条数:" + deleteCnt); 135 } 136 137 //Test API 5: excuteUpdate 执行DDL 语句删除表 138 @Test 139 public void test_excuteUpdate_drop(){ 140 String sql = "drop table tb_person"; 141 Query query = this.entityManager.createNativeQuery(sql); 142 int cnt = query.executeUpdate(); 143 System.out.println("cnt:" + cnt); 144 } 145 146 //Test API 6: 测试分页数据 147 @Test 148 public void test_page(){ 149 String jpql = "from PersonEntity"; 150 Query query = this.entityManager.createQuery(jpql); 151 query.setFirstResult(10); //序号从0 开始 152 query.setMaxResults(20); 153 List<PersonEntity> personList = query.getResultList(); 154 System.out.println("count:" + personList.size()); 155 for (PersonEntity personEntity : personList) { 156 System.out.println(personEntity); 157 } 158 } 159 160 //初始化100 条数据 161 @Test 162 public void test_savePerson(){ 163 Date date = new Date(); 164 for(int i=0; i<100; i++){ 165 PersonEntity person = new PersonEntity(); 166 person.setName("zhangsan" + i); 167 person.setAge(20); 168 person.setBirthday(date); 169 person.setBirthdayDate(date); 170 person.setBirthdayTime(date); 171 person.setEnglishScore(20.20); 172 person.setMathScore(89.8f); 173 person.setGender(Gender.BOY); 174 person.setInfo("I am a good boy".getBytes()); 175 this.entityManager.persist(person); 176 } 177 } 178 179 }
1. JPA 中sql 语句设置参数有两种形式,一种是命名参数,另一种是位置参数:
位置参数:执行效率高, 可读性差,可用于原生sql 和 JPQL 语句中, 格式《?+数字》,数字可任意指定 也可以不指定,直接用问号
命名参数:执行效率低 ,可读性强,可用于原生sql 和 JPQL 语句中, 格式《:+ 名称》,名称自定义
2. query.getSigleResult(); 方法必须确保查询结果有且只有一个放回对象。
3. query.excuteUpdate();方法可用于执行更新,删除语句
简单实体映射和EntityManagerAPI
【常用注解】
使用JPA 指定映射关系时,有两种方式,一种是使用xml 方式,另一种是注解方式,笔者推荐使用注解方式。在JPA 映射简单实体时,常用的注解如下:
@Entity:修饰实体类对象,表示该实体类对象进行数据库映射
@Table(name="***"):指定实体类对象映射的表名称
@Id: 指定主键
@GeneratedValue(strategy=GenerationType.AUTO):指定主键生成方式,默认为Auto。
IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式;
AUTO: JPA自动选择合适的策略,是默认选项;
SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 不支持这种方式
TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。
@Column(name="s_name",length=20,unique=true,nullable=false, insertable=true, updatable=true):修饰属性, 指定列名称和相关限制
@Enumerated(EnumType.STRING):修饰枚举类属性,
EnumType.STRING: 指定数据库中存储的是字符串类型,
EnumTypee.ORDINAL:指定数据库 存储的类型为枚举的索引(0,1,2,3...)
@Temporal(TemporalType.TIME):修饰日期类型:
TemporalType.DATE: 指定映射数据库中的DATE 类型,只存储日期
TemporalType.TIME: 指定映射数据库 中的TIME类型, 只存储时间
TemporalType.TIMESTAMP:指定映射数据库中的TIMESTAMP类型
@Transient:指定不映射的属性
@Lob:修饰 byte[] 数组,二进制文件
@Basic(fetch=FetchType.LAZY) : 默认注解,如果字段不添加任何注解,则默认添加了此注解。可以通过fetch 属性指定大数据字段延时加载,目前在Hibernate中并未能实现,或许是Hibernate的一个bug。
@NamedQueries({
@NamedQuery(name="***",query="*** hql ***"),
@NamedQuery(name="***",query="*** hql ***")
}): 命名查询注解,指定命名查询语句,query字段只能写JPQL 查询语句,不能写普通的sql 语句。
【简单实体对象:PersonEntity.java】
1 package org.zgf.jpa.entity; 2 3 import java.util.Date; 4 5 import javax.persistence.Basic; 6 import javax.persistence.Column; 7 import javax.persistence.Entity; 8 import javax.persistence.EnumType; 9 import javax.persistence.Enumerated; 10 import javax.persistence.FetchType; 11 import javax.persistence.GeneratedValue; 12 import javax.persistence.GenerationType; 13 import javax.persistence.Id; 14 import javax.persistence.Lob; 15 import javax.persistence.NamedQueries; 16 import javax.persistence.NamedQuery; 17 import javax.persistence.Table; 18 import javax.persistence.Temporal; 19 import javax.persistence.TemporalType; 20 import javax.persistence.Transient; 21 22 import org.zgf.jpa.enumer.Gender; 23 24 25 26 @Table(name="tb_person") 27 @Entity 28 @NamedQueries({ //命名查询注解:只能写JPQL 语句 29 @NamedQuery(name="queryAllByJpql",query="from PersonEntity personEntity"), 30 @NamedQuery(name="queryByName",query="from PersonEntity personEntity where personEntity.name = :name") 31 }) 32 public class PersonEntity { 33 34 /** 35 * 测试主键生成策略 36 * GenerationType.AUTO: 根据数据库的默认规则来生成主键 37 * GenerationType.IDENTITY:数据库自增(mysql 适用,Oracle不适用) 38 * GenerationType.SEQUENCE:序列生成方式,(Oracle适用,mysql 不适用) 39 */ 40 @Id 41 @GeneratedValue(strategy=GenerationType.AUTO) 42 private Integer id; 43 44 /*** 45 * 数据库字段限制: 46 * Column可以指定数据库 字段的名称 ,长度,唯一性,是否可以为空,是否可以插入,是否可以更新 47 */ 48 @Column(name="s_name",length=20,unique=true,nullable=false, insertable=true, updatable=true) 49 private String name; 50 private int age; 51 52 /** 53 * 枚举类型: 54 * EnumType.STRING: 指定数据库中存储的是字符串类型 55 * EnumTypee.ORDINAL:指定数据库 存储的类型为枚举的索引 56 */ 57 @Enumerated(EnumType.STRING) 58 private Gender gender; 59 60 private Double englishScore; 61 private Float mathScore; 62 63 /** 64 * 日期类型: TimeStamp 会根据当地的时间做自动转换 65 * TemporalType.DATE: 指定映射数据库中的DATE 类型,只存储日期 66 * TemporalType.TIME: 指定映射数据库 中的TIME类型, 只存储时间 67 * TemporalType.TIMESTAMP:指定映射数据库中的TIMESTAMP类型 68 */ 69 @Temporal(TemporalType.TIME) 70 private Date birthdayTime; 71 @Temporal(TemporalType.DATE) 72 private Date birthdayDate; 73 @Temporal(TemporalType.TIMESTAMP) 74 private Date birthday; 75 76 @Lob //大数据文件 77 @Basic(fetch=FetchType.LAZY, optional=true) //延迟加载为true,貌似是Hibernate的一个bug,并不能实现延迟加载 78 private byte[] info; 79 80 @Transient //不映射 此字段 81 private String transientProperty; 82 83 public Integer getId() { 84 return id; 85 } 86 87 public void setId(Integer id) { 88 this.id = id; 89 } 90 91 public String getName() { 92 return name; 93 } 94 95 public void setName(String name) { 96 this.name = name; 97 } 98 99 public int getAge() { 100 return age; 101 } 102 103 public void setAge(int age) { 104 this.age = age; 105 } 106 107 public Gender getGender() { 108 return gender; 109 } 110 111 public void setGender(Gender gender) { 112 this.gender = gender; 113 } 114 115 public Double getEnglishScore() { 116 return englishScore; 117 } 118 119 public void setEnglishScore(Double englishScore) { 120 this.englishScore = englishScore; 121 } 122 123 public Float getMathScore() { 124 return mathScore; 125 } 126 127 public void setMathScore(Float mathScore) { 128 this.mathScore = mathScore; 129 } 130 131 public Date getBirthdayTime() { 132 return birthdayTime; 133 } 134 135 public void setBirthdayTime(Date birthdayTime) { 136 this.birthdayTime = birthdayTime; 137 } 138 139 public Date getBirthdayDate() { 140 return birthdayDate; 141 } 142 143 public void setBirthdayDate(Date birthdayDate) { 144 this.birthdayDate = birthdayDate; 145 } 146 147 public Date getBirthday() { 148 return birthday; 149 } 150 151 public void setBirthday(Date birthday) { 152 this.birthday = birthday; 153 } 154 155 public byte[] getInfo() { 156 return info; 157 } 158 159 public void setInfo(byte[] info) { 160 this.info = info; 161 } 162 163 public String getTransientProperty() { 164 return transientProperty; 165 } 166 167 public void setTransientProperty(String transientProperty) { 168 this.transientProperty = transientProperty; 169 } 175 176 }
【EntityManger 常用API 使用方法:Test_PersonEntity.java】
1 package org.zgf.jpa.entity; 2 3 import java.util.ArrayList; 4 import java.util.Date; 5 import java.util.List; 6 7 import javax.persistence.Query; 8 9 import org.junit.Test; 10 import org.zgf.jpa.enumer.Gender; 11 /** 12 * @Description: 主要测试EntityManager类提供的常用接口 13 * @author zonggf 14 * @date 2015年11月4日-下午3:38:14 15 */ 16 public class Test_PersonEntity extends BasicJPATest { 17 18 private PersonEntity getPersonEntity(){ 19 Date date = new Date(); 20 PersonEntity person = new PersonEntity(); 21 person.setName("zhangsan"); 22 person.setAge(20); 23 person.setBirthday(date); 24 person.setBirthdayDate(date); 25 person.setBirthdayTime(date); 26 person.setEnglishScore(20.20); 27 person.setMathScore(89.8f); 28 person.setGender(Gender.BOY); 29 StringBuffer sb = new StringBuffer(); 30 for(int i=0; i<100;i++){ 31 sb.append("heladn asndsk nasfjgnas nsd gaksn a sdnkg asdn asndk asnd ansf asd n asngjka s"); 32 } 33 person.setInfo(sb.toString().getBytes()); 34 return person; 35 } 36 37 //Test API 1: 测试persiste 38 @Test 39 public void test_persist(){ 40 PersonEntity personEntity = getPersonEntity(); 41 this.entityManager.persist(personEntity); 42 } 43 44 //Test API 2:测试remove接口 45 @Test 46 public void test_remove_right(){ 47 //正确删除方式,必须先从数据库中查询,然后再进行删除,而且查询结果不能为空 48 PersonEntity personEntity = new PersonEntity(); 49 personEntity.setId(3); 50 personEntity = this.entityManager.find(PersonEntity.class, personEntity.getId()); 51 if(null != personEntity){ 52 this.entityManager.remove(personEntity);; 53 } 54 } 55 56 //Test API 2:测试remove接口 57 @Test(expected=java.lang.IllegalArgumentException.class) 58 public void test_remove_wrong(){ 59 //如果是自己创建的实体 对象,使用remove方式的时候,将会报错 60 PersonEntity personEntity = getPersonEntity(); 61 personEntity.setId(3); 62 this.entityManager.remove(personEntity);; 63 } 64 65 //Test API 3:测试mege 接口 66 @Test 67 public void test_merge(){ 68 //先根据主键进行查询,然后根据主键进行更新 69 PersonEntity personEntity = getPersonEntity(); 70 personEntity.setId(7); 71 personEntity.setName("zhangsan_merge" + 172); 72 this.entityManager.merge(personEntity); 73 } 74 75 //Test API 4:测试find 接口 76 @Test 77 public void test_find(){ 78 //find 方法,是根据主键进行查询的,所以传的参数必须是主键 79 int id = 5; 80 PersonEntity person = this.entityManager.find(PersonEntity.class, id); 81 System.out.println(person); 82 } 83 84 85 //Test API 5:测试createQuery(String jpql)接口 86 @Test 87 public void test_createQuery_String(){ 88 String jpql = "from PersonEntity personEntity"; 89 Query query = this.entityManager.createQuery(jpql); 90 List<PersonEntity> personList = query.getResultList(); 91 for (PersonEntity personEntity : personList) { 92 System.out.println(personEntity); 93 } 94 } 95 96 //Test API 6:测试命名查询 97 @Test 98 public void test_namedQuery_jpql(){ 99 String namedQueryName = "queryByName"; 100 Query query = this.entityManager.createNamedQuery(namedQueryName); 101 //设置命名参数 102 query.setParameter("name", "zhangsan"); 103 PersonEntity person = (PersonEntity) query.getSingleResult(); 104 System.out.println(person); 105 } 106 107 //Test API 7:测试createNativeQuery(String sql)接口 108 @Test 109 public void test_sqlQuery(){ 110 List<PersonEntity> personList = new ArrayList<>(); 111 String sql = "select p.id, p.s_name, p.age from tb_person p"; 112 Query query = this.entityManager.createNativeQuery(sql); 113 List list = query.getResultList(); 114 for(int i=0;i<list.size(); i++){ 115 PersonEntity person = new PersonEntity(); 116 Object[] objectArray = (Object[]) list.get(i); 117 person.setId((Integer)objectArray[0]); 118 person.setName((String)objectArray[1]); 119 person.setAge((Integer)objectArray[2]); 120 personList.add(person); 121 } 122 for(PersonEntity personEntity: personList){ 123 System.out.println(personEntity); 124 } 125 } 126 }
【辅助类:Gender.java】
1 package org.zgf.jpa.enumer; 2 /** 3 * @Description: 枚举类,映射到数据库中的是BOY 而不是"男" 4 * @author zonggf 5 * @date 2015年11月4日-下午3:39:36 6 */ 7 public enum Gender { 8 9 BOY("男"),GIRL("女"); 10 11 private String gender; 12 13 Gender(String gender){ 14 this.gender = gender; 15 } 16 17 public static Gender getGender(String gender){ 18 Gender[] genderEnumers = Gender.values(); 19 for (Gender genderEnumer : genderEnumers) { 20 if(null != gender){ 21 if(gender.equals(genderEnumer.toString())){ 22 return genderEnumer; 23 } 24 } 25 } 26 return null; 27 } 28 29 @Override 30 public String toString() { 31 return this.gender; 32 } 33 }
【辅助类:BasicJPATest.java】
1 package org.zgf.jpa.entity; 2 3 4 import javax.persistence.EntityManager; 5 import javax.persistence.EntityManagerFactory; 6 import javax.persistence.Persistence; 7 import javax.persistence.Query; 8 9 import org.junit.After; 10 import org.junit.Before; 11 import org.junit.Test; 12 /** 13 * 这是一个JPA 的测试类, 该类的功能如下: 14 * 1. 在每个测试方法之前调用setup 方法,开启事务并获取entityManager 对象。 15 * 2. 在每个测试方法之后调用teardown 方法, 提交事务,并关闭entityManager 对象 16 * @author Silence 17 */ 18 public abstract class BasicJPATest { 19 protected EntityManagerFactory entityManagerFactory; 20 protected EntityManager entityManager; 21 22 //子类的每个测试方法之前都会调用 23 @Before 24 public void setup(){ 25 this.entityManagerFactory = Persistence.createEntityManagerFactory("myJPA"); 26 this.entityManager = this.entityManagerFactory.createEntityManager(); 27 this.entityManager.getTransaction().begin(); 28 } 29 30 //子类的每个测试方法之后都会调用 31 @After 32 public void tearDown(){ 33 try{ 34 this.entityManager.getTransaction().commit(); 35 }catch(Exception ex){ 36 System.out.println("提交事务等阶段出现了错误哦"); 37 }finally{ 38 this.entityManager.close(); 39 this.entityManagerFactory.close(); 40 } 41 42 } 43 44 @Test 45 public void testCreateTables(){ 46 System.out.println("数据库表创建成功。。。"); 47 } 48 49 public void recoverData(){ 50 //清空表 51 String deleteSql = "delete Person"; 52 Query query = this.entityManager.createQuery(deleteSql); 53 query.executeUpdate(); 54 55 } 56 }
【注意】
1. @Basic(fetch=FetchType.LAZY) 注解在Hibernate 中不能实现,貌似是Hibernate 的一个bug
2. @Enumerated(EnumType.STRING) 注解映射的是枚举类的Key ,不是value