Spring+Jersey+JPA+Hibernate+MySQL实现CRUD操作案例

Posted NIITYZU

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring+Jersey+JPA+Hibernate+MySQL实现CRUD操作案例相关的知识,希望对你有一定的参考价值。

本文承接我的另一篇博文:Spring+Jersey+Hibernate+MySQL+HTML实现用户信息增删改查案例(附Jersey单元测试),主要更改内容如下:

  • Spring配置文件applicationContext中原先使用的是Hibernate,现在改为Hibernate对JPA的支持;
  • 增加了C3P0连接池;
  • 修改了Dao操作实现,改为Spring接管的JPA实现。
如果读者想详细查看Spring整合Jersey与前端交互可以点击上述连接。本文主要介绍以上三处修改内容,并且使用Jersey Test测试整合结果正确性。博文最后提供源码下载。至于JPA的介绍也不是本文的重点,若读者有不明白的地方可以查看其它文档。

一、添加pom依赖

因为整个项目构建是基于Maven的,所以在使用JPA和C3P0连接池之前,必须先添加相应的依赖JAR包,具体如下:

		<!-- hibernate对JPA支持 -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.3.11.Final</version>
		</dependency>

		<!-- hibernate C3P0连接池包 -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-c3p0</artifactId>
			<version>4.3.11.Final</version>
		</dependency>

		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.2.1</version>
		</dependency>

		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>mchange-commons-java</artifactId>
			<version>0.2.3.4</version>
		</dependency>

          目前比较成熟的 JPA 框架主要包括 Jboss 的 Hibernate EntityManager、Oracle 捐献给 Eclipse 社区的 EclipseLink、Apache 的 OpenJPA 等,如上所示,本文使用的是Hibernate的JPA支持。

二、修改applicationContext.xml

        正如Spring接管Hibernate一样,JAP也可以由Spring接管,只需要在applicationContext.xml文件做相应的配置即可,用户便无需关注诸如事务、安全等非业务逻辑的处理及实现。如下为修改后的Spring配置文件。

<?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:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	<!-- 注解支持 -->
	<context:annotation-config />

	<!-- 启动组件扫描 -->
	<context:component-scan
		base-package="com.spring.jersy.jpa.hibernate.resource,com.spring.jersy.jpa.hibernate.dao,com.spring.jersy.jpa.hibernate.service" />

	<!-- JDBC属性文件位置 -->
	<context:property-placeholder
		location="classpath:com/spring/jersy/jpa/hibernate/config/jdbc.properties" />

	<!-- 数据库连接 -->
	<!-- 如果需要配置连接池org.apache.commons.dbcp.BasicDataSource则不符合 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="jdbcUrl" value="${jdbc.url}" />
		<property name="user" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		
		<!-- hibernate.c3p0.min_size: 数据库连接池的最小连接数 -->
		<property name="minPoolSize" value="${jdbc.minPoolSize}" />
		
		<!-- hibernate.c3p0.max_size: 数据库连接池的最大连接数 -->
		<property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
		
		<!-- 指定连接池的初始化连接数  取值应在minPoolSize 与 maxPoolSize 之间.Default:3--> 
		<property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>  
		
		<!-- 缓存 Statement 对象的数量 -->
		<property name="maxStatements" value="${jdbc.maxStatements}" />
		
		<!-- 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁 -->
		<property name="maxIdleTime" value="${jdbc.maxIdleTime}" />
		
		<!-- 表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时 -->
		<property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}" />
		
		<!-- 当数据库连接池中的连接耗尽时, 同一时刻获取多少个数据库连接 -->
		<property name="acquireIncrement" value="${jdbc.acquireIncrement}" />	
	</bean>

	<!-- Hibernate对Jpa的实现 -->
	<bean id="hibernateJpaVendorAdapter"
		class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

	<!-- Jpa 事务管理器 -->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<!-- 定义实体管理器工厂 Jpa配置 LocalContainerEntityManagerFactoryBean这个选项Spring扮演了容器的角色。完全掌管JPA -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<!-- 指定数据源 -->
		<property name="dataSource" ref="dataSource" />
		<!-- 指定Jpa持久化实现厂商类,这里以Hibernate为例 -->
		<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
		<!-- 指定Entity实体类包路径 -->
		<property name="packagesToScan">
			<array>
				<value>com.spring.jersy.jpa.hibernate.model</value>
			</array>
		</property>
		<!-- 指定JPA属性;如Hibernate中指定是否显示SQL的是否显示、方言等 -->
		<property name="jpaProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.mysqlDialect</prop>
				<prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.format_sql">true</prop>
			</props>
		</property>
	</bean>

	<!-- 配置事务特性,配置add,delete,update开始的方法,事务传播特性为required -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="*" read-only="true" />
		</tx:attributes>
	</tx:advice>

	<!-- 配置那些类的方法进行事务管理,当前<span style="font-family: Arial, Helvetica, sans-serif;">com.spring.jersy.jpa.hibernate.service</span>包中的子包, 类中所有方法需要,还需要参考tx:advice的设置 -->
	<aop:config>
		<aop:pointcut id="allManagerMethod"
			expression="execution(* com.spring.jersy.jpa.hibernate.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod" />
	</aop:config>
</beans>
        jdbc.properties内容如下:

#MySQL jdbc
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root

#c3p0
jdbc.minPoolSize=5
jdbc.maxPoolSize=20
jdbc.initialPoolSize=15 
jdbc.maxStatements=50
jdbc.maxIdleTime=300
jdbc.idleConnectionTestPeriod=60  
jdbc.acquireIncrement=2
三、修改Dao实现

        之前使用Hibernate作为持久层框架时,Spring提供了HibernateTemplate对数据库的CRUD等各种操作的实现。现在我们使用JPA操作时,Spring在配置文件中实现了EntityManagerFactory的注入实现,但是并没有提供EntityManager的实现。如果每次都要使用工厂类生成实体管理器,则非常不利于开发。JPA提供了@PersistenceContext注解才解决这个问题。如下:

package com.spring.jersy.jpa.hibernate.dao;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;

import com.spring.jersy.jpa.hibernate.model.User;

@Repository(value = "userJpaDao")
public class UserJpaDao {

	//通过该注解,在类中便无需EntityManagerFactory创建EntityManager了。
	@PersistenceContext
	private EntityManager entityManager;

	// 根据用户名和密码查询
	public List<User> findByNameAndPassword(User user) {
		
		//如果sql语句中 select 有部分字段或者全部字段,返回的都是对象数组
		String jql = "select u " +
				" from User u where u.username=:username and u.password = :password";
		Query query = entityManager.createQuery(jql);
		query.setParameter("username", user.getUsername());
		query.setParameter("password", user.getPassword());
		@SuppressWarnings("unchecked")
		List<User> listUsers = (List<User>) query.getResultList();
		return listUsers;
	}

	//根据姓名查询
	@SuppressWarnings("unchecked")
	public List<User> findUserByName(String name) {
		List<User> userList = new ArrayList<User>();
		String jql = "from User u where u.username like:name";
		Query query = entityManager.createQuery(jql);
		query.setParameter("name", name);
		userList = (List<User>) query.getSingleResult();
		return userList;
	}

	//添加用户
	public boolean addUser(User user) {
		try {
			entityManager.persist(user);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	//删除用户
	public boolean deleteUser(Integer id) {
		try {
			User user = entityManager.find(User.class, id);
			if(user != null){
				entityManager.remove(user);
				return true;
			}else {
				return false;
			}
			
		} catch (Exception e) {
			return false;
		}
	}

	//修改用户
	public boolean updateUser(User user){
		try {
		
			String jql = "update User u set u.username = :name, u.password = :pwd, " +
					"u.address = :address, u.tel =:tel where u.id = :id";
			Query query = entityManager.createQuery(jql);
			query.setParameter("name", user.getUsername())
			     .setParameter("pwd", user.getPassword())
			     .setParameter("address", user.getAddress())
			     .setParameter("tel", user.getTel())
			     .setParameter("id", user.getId());
			
		    if(query.executeUpdate() == 1){
		    	return true;
		    }else {
		    	return false;
		    }
			
		} catch (Exception e) {
			return false;
		}
	}
}
四、业务层实现

        业务层实现不需要太多修改,只需引用JPA Dao实现即可。具体如下:

package com.spring.jersy.jpa.hibernate.service;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.spring.jersy.jpa.hibernate.dao.UserJpaDao;
import com.spring.jersy.jpa.hibernate.model.User;


@Service(value="userService")
public class UserService {

	@Resource(name="userJpaDao")
	private UserJpaDao userJpaDao;
	
	public User findUserByNameAndPassword (User user) {
		List<User> listUsers = userJpaDao.findByNameAndPassword(user);
		
		if(listUsers.size() > 0) {
			return listUsers.get(0);
		}
		return null;
	}
	
	public User findUserByName (String name) {
        List<User> listUsers = userJpaDao.findUserByName(name);
		if(listUsers.size() > 0) {
			return listUsers.get(0);
		}
		return null;
	}
	
	public boolean deleteUser(Integer id) {
		return userJpaDao.deleteUser(id);
	}
	
	public boolean addUser(User user) {
		return userJpaDao.addUser(user);
	}
	
	public boolean updateUser(User user){
		return userJpaDao.updateUser(user);
	}

	public UserJpaDao getUserJpaDao() {
		return userJpaDao;
	}

	public void setUserJpaDao(UserJpaDao userJpaDao) {
		this.userJpaDao = userJpaDao;
	}
}
五、资源类

         资源类实现主要是Jersey,本文因为上接Spring+Jersey+Hibernate+MySQL+HTML实现用户信息增删改查案例(附Jersey单元测试)文章,此处Jersey资源类不作任何修改,具体如下:

package com.spring.jersy.hibernate.resource;

import javax.annotation.Resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import net.sf.json.JSONObject;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.spring.jersy.hibernate.model.User;
import com.spring.jersy.hibernate.service.UserService;

/**
 * Spring 使用Component注解Jersey资源类
 * Path:相当于Spring MVC中的RequestMapping,用于HTTP URL请求
 * Scope:表示Spring的单例模式或者原型模式prototype
 */
@Component
@Path("/user")
@Scope("prototype")
public class UserResource {

	@Resource(name = "userService")
	private UserService userService;
	private String message;

	/**
	 * GET:表示Jersey rest查询请求
	 * Path:此处完整路径需接类上面的Path,如:user/exist/xx/1233,才会跳转到该方法处理
	 * Consumes:表示前端请求数据格式
	 * Produces:表示返回值数据格式
	 * PathParam:用于注解参数变量,与URL中对应的变量名对应,达到传递的作用
	 */
	@GET
	@Path("/exist/{username}/{password}")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	//查询
	public String isExist(@PathParam("username") String username,
			@PathParam("password") String password) {

		User user = new User();
		user.setUsername(username);
		user.setPassword(password);
		User result = userService.findUserByNameAndPassword(user);
		
		/**
		 * Spring+Jersey框架,不会主动帮助我们将传递的对象转换成JSON数据格式,
		 * 需使用JSON lib类中的JSONObject或者JSONArray转换才能够传递,否则前端
		 * 会报302错误。
		 */
		JSONObject jsonUser = JSONObject.fromObject(result);
		return jsonUser.toString();
	}

	@POST
	@Path("/addUser")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	//添加
	public String addUser(User user) {

		boolean flag = userService.addUser(user);
		if (flag) {
			message = "success";
		} else {
			message = "fail";
		}
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("message", message);
		return jsonObject.toString();
	}

	@DELETE
	@Path("/deleteUser/{id}")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	//删除
	public String deleteUser(@PathParam("id") Integer id) {

		boolean flag = userService.deleteUser(id);
		if (flag) {
			message = "success";
		} else {
			message = "fail";
		}
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("message", message);
		return jsonObject.toString();
	}

	@PUT
	@Path("/updateUser/{id}")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	//修改
	public String updateUser(@PathParam("id") Integer id, User user) {
		user.setId(id);
		boolean flag = userService.updateUser(user);
		if (flag) {
			message = "success";
		} else {
			message = "fail";
		}
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("message", message);
		return jsonObject.toString();
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}
}
六、单元测试

        此处使用的单元测试是Jersey Test,测试类内容如下:

package com.spring.jersy.hibernate.test;

import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.request.RequestContextListener;
import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.WebAppDescriptor;

/**
 * 主要加载web.xml中的配置信息
 * @author Administrator
 *
 */
public abstract class BaseJerseyServiceTest extends JerseyTest {
	    @Override
	    protected WebAppDescriptor configure() {
	        return  new WebAppDescriptor.Builder("com.spring.jersy.jpa.hibernate.resource")
	        .contextParam( "contextConfigLocation", "classpath:com/spring/jersy/hibernate/config/applicationContext.xml")
	        //.servletClass(SpringServlet.class)
	        .filterClass(SpringServlet.class)
	        .initParam("com.sun.jersey.config.feature.Redirect", "true")
	        .initParam("com.sun.jersey.config.feature.FilterForwardOn404", "true")
	        .initParam("com.sun.jersey.config.property.WebPageContentRegex", "/(images|css|jsp)/.*")
	        .initParam("com.sun.jersey.api.json.POJOMappingFeature", "true")
	        .initParam("com.sun.jersey.config.property.packages", "com.spring.jersy.jpa.hibernate.resource")
	        .contextListenerClass(ContextLoaderListener.class)
	        .requestListenerClass(RequestContextListener.class)
	        .build();
	    }
}
package com.spring.jersy.jpa.hibernate.test;


import javax.ws.rs.core.MediaType;
import org.junit.Before;
import org.junit.Test;
import com.spring.jersy.jpa.hibernate.model.User;
import com.sun.jersey.api.client.WebResource;
public class UserResourceTest extends BaseJerseyServiceTest {

	private WebResource webResource;

	@Before
	public void setUp() throws Exception {
		webResource = resource();
		// webResource.accept("application/json");
	}

	@Test
	public void testIsExist() {
		String response = webResource.path("/user/exist/xx/1233").get(
				String.class);
		System.out.println(response);
	}

	@Test
	public void testAddUser() {

		User user = new User();
		user.setUsername("wwwx");
		user.setPassword("pwd");
		user.setAddress("江苏盐城");
		user.setTel("12333");
		String response = webResource.path("/user/addUser")
				.entity(user, "application/json").post(String.class, user);
		System.out.println(response.toString());
	}

	@Test
	public void testDeleteUser() {
		String response = webResource.path("/user/deleteUser/59").delete(
				String.class);
		System.out.println(response.toString());
	}

	@Test
	public void testUpdateUser() {
		User user = new User();
		user.setUsername("王三儿");
		user.setPassword("wangsaner");
		String response = webResource.path("/user/updateUser/64")
				.entity(user, MediaType.APPLICATION_JSON).put(String.class);
		System.out.println(response.toString());
	}
}
七、测试结果如下:

Hibernate: 
    insert 
    into
        test.user
        (username, password, address, tel) 
    values
        (?, ?, ?, ?)
{"message":"success"}

Hibernate: 
    select
        user0_.id as id1_2_0_,
        user0_.username as username2_2_0_,
        user0_.password as password3_2_0_,
        user0_.address as address4_2_0_,
        user0_.tel as tel5_2_0_ 
    from
        test.user user0_ 
    where
        user0_.id=?
{"message":"fail"}

Hibernate: 
    update
        test.user 
    set
        username=?,
        password=?,
        address=?,
        tel=? 
    where
        id=?

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.username as username2_2_,
        user0_.password as password3_2_,
        user0_.address as address4_2_,
        user0_.tel as tel5_2_ 
    from
        test.user user0_ 
    where
        user0_.username=? 
        and user0_.password=?
{"address":"ss","id":48,"password":"1233","tel":"1232333","username":"xx"}


附1:异常

          将项目部署到服务器上运行,在Web调试过程中,所有调试一切正常,但是在运行单元测试的时候报如下异常错误:

Caused by: java.lang.NoSuchMethodError: javax.persistence.Table.indexes()[Ljavax/persistence/Index;
	at org.hibernate.cfg.annotations.EntityBinder.processComplementaryTableDefinitions(EntityBinder.java:973)
	at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:824)
	at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3845)
	at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3799)
	at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1412)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
	at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849)
	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:343)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1625)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1562)
	... 45 more
         解决办法:具体原因是因为项目中存在其他版本的JPA实现,导致版本冲突,在调用javax.persistence.*时冲突,如Eclipse link,所以需要在pom.xml中去掉Eclipse 对JPA的支持。

		<!-- 增加Eclipse JPA Provider支持 -->
<!-- 		<dependency>
			<groupId>org.eclipse.persistence</groupId>
			<artifactId>org.eclipse.persistence.jpa</artifactId>
			<version>2.6.0</version>
		</dependency>
 -->
附2:源码下载

Spring+Jersey+JPA+Hibernate+MySQL整合






 



以上是关于Spring+Jersey+JPA+Hibernate+MySQL实现CRUD操作案例的主要内容,如果未能解决你的问题,请参考以下文章

使用 EJB + JPA + Jersey 进行延迟加载

为现有的 Jersey 项目从 Netbeans JPA 添加休眠

Jersey API + JPA/Hibernate Criteria延迟加载不起作用

JPA

如何使用 Jax-RS(Jersey) 在 Tomcat7 上运行应用程序 Hibernate 5.x、Jpa 2.1、Java EE7(javaee-api 7.0)

JPA(休眠)映射OneToMany不正确?