jpa/springdata(1)jpa
1.什么是jpa
假如学过hibernate在jpa会发现非常的简单,因为是同一个人写的,jpa是第三方orm框架的一种规范,hibernate作为jpa 的一个子集
2.需要导入的jar
这里使用的是hibernate作为orm
待续重写整个部分
3.jpa的配置简要说明
新建--jpa项目(自动生成jpa项目的xml文件)
persistence.xml,文件的名称是固定的,然后是根据name="jpa"创建EntityManagerFactory,这个类似于c3p0连接池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <persistence-unit name= "jpa" transaction- type = "RESOURCE_LOCAL" > <!-- 实际上配置的是 javax.persistence.spi.PersistenceProvider 接口的实现类, 若 JPA 项目中只有一个 JPA 的实现产品, 则也可以不配置该节点. --> <provider>org.hibernate.ejb.HibernatePersistence< /provider > <!-- 添加持久化类,类似hibernate的mapper或者config.addclass()方法 --> <class>com.jpa.helloworld.Item< /class > <!-- 配置二级缓存的策略 ALL:所有的实体类都被缓存 NONE:所有的实体类都不被缓存. ENABLE_SELECTIVE:标识 @Cacheable( true ) 注解的实体类将被缓存 DISABLE_SELECTIVE:缓存除标识 @Cacheable( false ) 以外的所有实体类 UNSPECIFIED:默认值,JPA 产品默认值将被使用 --> <shared-cache-mode>ENABLE_SELECTIVE< /shared-cache-mode > <properties> <!-- 连接数据库的基本信息 --> <property name= "javax.persistence.jdbc.driver" value= "com.mysql.jdbc.Driver" /> <property name= "javax.persistence.jdbc.url" value= "jdbc:mysql:///jpa" /> <property name= "javax.persistence.jdbc.user" value= "root" /> <property name= "javax.persistence.jdbc.password" value= "1230" /> <!-- 配置 JPA 实现产品的基本属性. 配置 hibernate 的基本属性,同hibernate的配置 --> <property name= "hibernate.format_sql" value= "true" /> <property name= "hibernate.show_sql" value= "true" /> <property name= "hibernate.hbm2ddl.auto" value= "update" /> <!-- 同hibernate二级缓存配置 --> <property name= "hibernate.cache.use_second_level_cache" value= "true" /> <property name= "hibernate.cache.region.factory_class" value= "org.hibernate.cache.ehcache.EhCacheRegionFactory" /> <property name= "hibernate.cache.use_query_cache" value= "true" /> < /properties > < /persistence-unit > |
如下配置也与hibernate类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public static void main(String[] args) { String persistenceUnitName = "jpa" ; // 与c3p0的配置类似的加载方法 Map<String, Object> properites = new HashMap<String, Object>(); properites.put( "hibernate.show_sql" , true ); EntityManagerFactory entityManagerFactory = // 在配置文件中配置好了直接获取 //Persistence .createEntityManagerFactory(persistenceUnitName); // 在put中放置需要设置的属性,假如在xml中已经配置,那么将覆盖xml中的属性 Persistence.createEntityManagerFactory(persistenceUnitName, properites); EntityManager entityManager = entityManagerFactory.createEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); // 开始事务 transaction.commit(); // 提交事物 entityManager.close(); entityManagerFactory.close(); } |
4.注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | @Table(name= "JPA_ORDERS" ) // 对应的表名 @Entity // 标注为实体类 public class Dept { private Integer id ; private String deptName; @GeneratedValue/*获取主键的方式,主键 id 的描述,在hibernate中,以及mybatis中的resultmap的都是描述为 id 标签,<br>这里获取主键的方式有IDENTITY:采用数据库 ID自增长的方式来自增主键段,Oracle 不支持这种方式;AUTO: JPA自动选择合适的策略,<br>是默认选项(因为是默认的选项所以也可以不写);SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql <br>不支持这种方式,TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。@GeneratedValue(strategy=GenerationType.AUTO), GenerationType.TABLE有些特殊但是避免了数据库切换不兼容的问题 @TableGenerator(name= "ID_GENERATOR" , // 与generator= "ID_GENERATOR" 的值一致 table= "jpa_id_generators" , // 数据库表的名称,这里是三列, id ,PK_NAME,PK_VALUE pkColumnName= "PK_NAME" , // 数据库表的对应的列的名称 pkColumnValue= "CUSTOMER_ID" , // 向对应列的名称的值 valueColumnName= "PK_VALUE" , // 数据库表的对应的列的名称 allocationSize=100) // 这里设置的值,PK_VALUE设置为100,每次 id 自增100 @GeneratedValue(strategy=GenerationType.TABLE,generator= "ID_GENERATOR" ) */ @Id public Integer getId() { return id ; } public void setId(Integer id ) { this. id = id ; } @Column(name= "DEPT_NAME" )/*对应数据库的名称,也是新建数据库的名称,假如数据库的名称与属性的名称一致,那么这类的注解也可以默认不写,<br> 在所有的默认的get方法上会默认的添加@Basic注解,假如在没有 set 方法的前提下会报错,假如现在有一个get方法,但是不需要与数据库相关联,<br> 那么在get方法上使用@Transient,jap处理注解在类方法上的注解,其余都在get方法上,Column还具有以下一些属性 ,unique(唯一) 、<br> nullable(不能为null) 、length (长度)等, @Column(name= "LAST_NAME" ,length=50,nullable= false )*/ public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } @Transient public void getInfo() { System.out.println(111); } } |
其他注解
@Temporal //时间匹配的格式
例如
1 2 3 4 | @Temporal(TemporalType.DATE) // 可选TemporalType.TIME,TemporalType.DATE,TemporalType.TIMESTAMP public java.util.Date getDate() { return date ; } |
5.常用api
1)EntityManagerFactory的常用方法
createEntityManager():用于创建实体管理器对象实例。
createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map 参数用于提供 EntityManager 的属性。
isOpen():检查 EntityManagerFactory 是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭。
close():关闭 EntityManagerFactory 。 EntityManagerFactory 关闭后将释放所有资源,isOpen()方法测试将返回 false,其它方法将不能调用,否则将导致IllegalStateException异常。
2)EntityManager的常用方法
1> find方法(同hibernate的get方法,用法基本一致)
Department dept = entityManager.find(Department.class, 1);//1表示更具id查询的
2> getReference方法(类似于hibernate的load方法,用法基本一致,延时加载,使用代理类)
3> persist方法(类似hibernate的persist方法,没有save方法,不能设置id)
entityManager.persist(mgr);
4> remove方法(类似于hibernate的delete方法)
5> merge方法(类似于updateorsave方法,但是又不同,在updateorsave方法中的session不能同时关联两个oid,而merge的entityManager可以这么去做)
1 2 3 4 | customer.setId(4); Customer customer2 = entityManager. find (Customer.class, 4); entityManager.merge(customer); System.out.println(customer == customer2); //false |
6> flush方法(类似hibernate的flush,不会提交事务,只是一个sql语句,事务没有提交,那么数据库的数值还是没变)
7> clear方法(类似hibernate的clear方法,清除一级缓存)
8> contains (Object entity) 方法 (判断entity实例是否属于当前持久上下文环境管理)
9> isOpen方法(判断实体管理器是否处于打开状态)
10> getTransaction方法(获取事物)
11> close方法(关闭)
12>refresh方法(类似hibernate的refresh方法,将持久化刷新到缓存)
13>createQuery(String sql) 方法(类似hibernate的createQuery方法)
14>createNativeQuery (String sqlString)方法()使用规范的sql语句
15>getTransaction方法(获取事物)
3)EntityTransaction的方法
1)begin ()用于启动一个事务,此后的多个数据库操作将作为整体被提交或撤消。若这时事务已启动则会抛出 IllegalStateException 异常。
2)commit ()用于提交当前事务。即将事务启动以后的所有数据库更新操作持久化至数据库中。
3)rollback ()撤消(回滚)当前事务。即撤消事务启动后的所有数据库更新操作,从而不对数据库产生影响。
4)setRollbackOnly ()使当前事务只能被撤消。
5)getRollbackOnly ()查看当前事务是否设置了只能撤消标志。
6)isActive ()查看当前事务是否是活动的。如果返回true则不能调用begin方法,否则将抛出 IllegalStateException 异常;如果返回 false 则不能调用 commit、rollback、setRollbackOnly 及 getRollbackOnly 方法,否则将抛出 IllegalStateException 异常
5.关系映射
1)1对1
1 2 3 4 5 6 7 8 9 10 | @JoinColumn(name= "MGR_ID" , unique= true ) // 对应列,unique= true 满足唯一 @OneToOne(fetch=FetchType.LAZY) //fetch =FetchType.LAZY延迟加载,存在的异常类似hibernate的load public Manager getMgr() { return mgr; } // 单向一对一 @OneToOne(mappedBy= "mgr" ) //mappedBy = "mgr" 表示维护的一端,没写默认都是对应主键的外键关联 public Department getDept() { return dept; } // 双向一对一 |
2)单向1对多
1 2 3 4 5 | @JoinColumn(name= "CUSTOMER_ID" ) @OneToMany(fetch=FetchType.LAZY,cascade={CascadeType.REMOVE},mappedBy= "customer" ) //cascade ={CascadeType.REMOVE}级联,mappedBy= "customer" 维护的一端 public Set<Order> getOrders() { return orders; } |
3)单向多对1
1 2 3 4 5 | @JoinColumn(name= "CUSTOMER_ID" ) @ManyToOne(fetch=FetchType.LAZY) public Customer getCustomer() { return customer; } |
4)双向1对多
2)和3)一起
5)多对多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @ManyToMany(mappedBy= "categories" ) public Set<Item> getItems() { return items; } @JoinTable(name= "ITEM_CATEGORY" , // @JoinTable 来映射中间表,name 指向中间表的名字,多对多是基于外表的 joinColumns={@JoinColumn(name= "ITEM_ID" , referencedColumnName= "ID" )}, //joinColumns 映射当前类所在的表在中间表中的外键,name 指定外键列的列名, referencedColumnName 指定外键列关联当前表的哪一列,inverseJoinColumns={@JoinColumn(name= "CATEGORY_ID" , referencedColumnName= "ID" )}) //inverseJoinColumns 映射关联的类所在中间表的外键 @ManyToMany public Set<Category> getCategories() { return categories; } |
6.二级缓存
<!--配置二级缓存的策略
ALL:所有的实体类都被缓存
NONE:所有的实体类都不被缓存.
ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存
DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类
UNSPECIFIED:默认值,JPA 产品默认值将被使用
-->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>//基本类似hibernate,原理和hibernate和mybatis差不多,前提需要开启被管理的orm框架的二级缓存
7.jpql和hibernate的hql的语法基本类似
1)createQuery方法:
1 2 3 4 5 | public void testPartlyProperties(){ String jpql = "SELECT new Customer(c.lastName, c.age) FROM Customer c WHERE c.id > ?" ;/*Customer c,根据Customer的注释可以找到表名,new Customer(c.lastName, c.age),<br>根据查询结果进行填充*/ List result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList(); System.out.println(result); } |
2)createNamedQuery方法//使用 @NamedQuery 标记的查询语句
public void testNamedQuery(){
Query query = entityManager.createNamedQuery("testNamedQuery").setParameter(1, 3);
Customer customer = (Customer) query.getSingleResult();
System.out.println(customer);
}
@NamedQuery(name="testNamedQuery", query="FROM Customer c WHERE c.id = ?")//使用这个之后才能使用createNamedQuery
@Cacheable(true)//开启缓存,兼与jpa的二级缓存策略
@Table(name="JPA_CUTOMERS")//表名
@Entity//实体
public class Customer {}
3)createNativeQuery方法//使用规范的sql
public void testNativeQuery(){
String sql = "SELECT age FROM jpa_cutomers WHERE id = ?";
Query query = entityManager.createNativeQuery(sql).setParameter(1, 3);
Object result = query.getSingleResult();
System.out.println(result);
}
4)查询缓存
String jpql = "FROM Customer c WHERE c.age > ?";
Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);/*需要开启二级缓存(见jpa配置),在二级缓存中查找,假如解析后的sql语句一致,不会发送sql,直接使用缓存中的数据*/
5)排序与分组
分组
String jpql = "SELECT o.customer FROM Order o GROUP BY o.customer HAVING count(o.id) >= 2;
List<Customer> customers = entityManager.createQuery(jpql).getResultList();
排序
String jpql = "FROM Customer c WHERE c.age > ? ORDER BY c.age DESC";
Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);
6)连表查询
String jpql = "FROM Customer c LEFT OUTER JOIN FETCH c.orders WHERE c.id = ?"; //其他一致,内联接,右连接
Customer customer = (Customer) entityManager.createQuery(jpql).setParameter(1, 12).getSingleResult();
7)子查询
String jpql = "SELECT o FROM Order o WHERE o.customer = (SELECT c FROM Customer c WHERE c.lastName = ?)";
Query query = entityManager.createQuery(jpql).setParameter(1, "YY");
8)字符串处理函数:
concat(String s1, String s2):字符串合并/连接函数。
substring(String s, int start, int length):取字串函数。
trim([leading|trailing|both,] [char c,] String s):从字符串中去掉首/尾指定的字符或空格。
lower(String s):将字符串转换成小写形式。
upper(String s):将字符串转换成大写形式。
length(String s):求字符串的长度。
locate(String s1, String s2[, int start]):从第一个字符串中查找第二个字符串(子串)出现的位置。若未找到则返回0。
备注:其它基本上与hql一致,个人还是写sql写的比较多,然后使用类的方式也有//类找表
8.spring整合jpa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <?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" xsi:schemaLocation="http: //www .springframework.org /schema/beans http: //www .springframework.org /schema/beans/spring-beans .xsd http: //www .springframework.org /schema/context http: //www .springframework.org /schema/context/spring-context-4 .0.xsd http: //www .springframework.org /schema/tx http: //www .springframework.org /schema/tx/spring-tx-4 .0.xsd"> <!-- 配置自动扫描的包 --> <context:component-scan base-package= "com.jpa" >< /context :component-scan> <context:property-placeholder location= "classpath:db.properties" /> <bean id = "dataSource" class= "com.mchange.v2.c3p0.ComboPooledDataSource" > <property name= "user" value= "${jdbc.user}" >< /property > <property name= "password" value= "${jdbc.password}" >< /property > <property name= "driverClass" value= "${jdbc.driverClass}" >< /property > <property name= "jdbcUrl" value= "${jdbc.jdbcUrl}" >< /property > <!-- 配置其他属性 --> < /bean > <!-- 配置 EntityManagerFactory --> <bean id = "entityManagerFactory" class= "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <!-- 配置数据源 --><br> <property name= "dataSource" ref= "dataSource" >< /property > <!-- 配置 JPA 提供商的适配器. 可以通过内部 bean 的方式来配置 --> <property name= "jpaVendorAdapter" > <bean class= "org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" >< /bean > < /property > <!-- 扫描指定包下的@entitiy --> <property name= "packagesToScan" value= "com.jpa.spring.entities" >< /property > <!-- 配置 JPA 的基本属性. 例如 JPA 实现产品的属性(根据提供商的适配器) --> <property name= "jpaProperties" > <props> <prop key= "hibernate.show_sql" > true < /prop > <prop key= "hibernate.format_sql" > true < /prop > <prop key= "hibernate.hbm2ddl.auto" >update< /prop > < /props > < /property > < /bean > <!-- 配置 JPA 使用的事务管理器 --> <bean id = "transactionManager" class= "org.springframework.orm.jpa.JpaTransactionManager" > <property name= "entityManagerFactory" ref= "entityManagerFactory" >< /property > < /bean > <!-- 配置支持基于注解是事务配置 --> <tx:annotation-driven transaction-manager= "transactionManager" /> < /beans > |
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | public class Test { private static ApplicationContext ctx=null; { ctx= new ClassPathXmlApplicationContext( "application.xml" ); } @org.junit.Test public void testC3p0(){ ComboPooledDataSource bean = ctx.getBean(ComboPooledDataSource.class); System.out.println(bean); } @org.junit.Test public void testEntity() throws SQLException{ LocalContainerEntityManagerFactoryBean lcemf = ctx.getBean(LocalContainerEntityManagerFactoryBean.class); DataSource dataSource = lcemf.getDataSource(); Connection connection = dataSource.getConnection(); System.out.println(connection); } } @org.junit.Test public void testEntity() throws SQLException{ LocalContainerEntityManagerFactoryBean lcemf = ctx.getBean(LocalContainerEntityManagerFactoryBean.class); // DataSource dataSource = lcemf.getDataSource(); // Class<? extends EntityManager> entityManagerInterface = lcemf.getEntityManagerInterface(); EntityManagerFactory en = lcemf.getNativeEntityManagerFactory(); EntityManager manager = en.createEntityManager(); EntityTransaction transaction = manager.getTransaction(); transaction.begin(); Person person = new Person(); person.setBrith(new Date()); person.setEamil( "11@11" ); person.setLastName( "john" ); manager.persist(person); transaction.commit(); manager.close(); en.close(); // if (lcemf instanceof EntityManager){ //false // System.out.println( true ); // } // Connection connection = dataSource.getConnection(); // System.out.println(connection); } |
综上所述有以下的结论
1 2 3 4 5 6 7 8 9 10 11 | @Repository public class PersonDao { @PersistenceContext // 上述测试类的一系列代码,应为LocalContainerEntityManagerFactoryBean不是EntityManager private EntityManager entityManager; public void save(Person person){ entityManager.persist(person); } } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· 本地部署 DeepSeek:小白也能轻松搞定!
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 在缓慢中沉淀,在挑战中重生!2024个人总结!
· 大人,时代变了! 赶快把自有业务的本地AI“模型”训练起来!
· 从 Windows Forms 到微服务的经验教训