首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

Spring + Jersey + JPA + Hibernate + MySQL 實現 CRUD 完整整合案例

     編輯:MySQL 綜合教學

企業級 RESTful 服務棧:Spring 管理 JPA/Hibernate + C3P0 連接池 + Jersey 端點

架構調整點

  • Hibernate → Hibernate EntityManager (JPA 實現)
  • 新增 C3P0 連接池 替換 DBCP
  • DAO 層改用 Spring 注入 EntityManager 實現
  • 使用 Jersey Test 單元測試驗證整合正確性

一、Maven 依賴配置

<dependencies>
    <!-- Hibernate JPA 支援 -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>4.3.11.Final</version>
    </dependency>
    <!-- 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>
    <!-- MySQL 驅動、Spring、Jersey 等略 -->
</dependencies>

二、Spring 配置 applicationContext.xml

<beans ...>
    <context:annotation-config/>
    <context:component-scan base-package="com.spring.jersy.jpa.hibernate.resource,dao,service"/>
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- C3P0 數據源 -->
    <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}"/>
        <property name="minPoolSize" value="${jdbc.minPoolSize}"/>
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
        <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>
        <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>

    <!-- JPA 廠商適配器 -->
    <bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>

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

    <!-- EntityManagerFactory -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
        <property name="packagesToScan">
            <array><value>com.spring.jersy.jpa.hibernate.model</value></array>
        </property>
        <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>

    <!-- 事務增強 -->
    <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>
    <aop:config>
        <aop:pointcut expression="execution(* com.spring.jersy.jpa.hibernate.service..*(..))" id="allManagerMethod"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod"/>
    </aop:config>
</beans>

jdbc.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root
jdbc.minPoolSize=5
jdbc.maxPoolSize=20
jdbc.initialPoolSize=15
jdbc.maxStatements=50
jdbc.maxIdleTime=300
jdbc.idleConnectionTestPeriod=60
jdbc.acquireIncrement=2

三、DAO 實現:@PersistenceContext 注入 EntityManager

@Repository("userJpaDao")
public class UserJpaDao {
    @PersistenceContext
    private EntityManager entityManager;

    public List<User> findByNameAndPassword(User user) {
        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());
        return query.getResultList();
    }

    public List<User> findUserByName(String name) {
        String jql = "FROM User u WHERE u.username LIKE :name";
        Query query = entityManager.createQuery(jql);
        query.setParameter("name", name);
        return query.getResultList();
    }

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

    public boolean deleteUser(Integer id) {
        try {
            User user = entityManager.find(User.class, id);
            if (user != null) { entityManager.remove(user); return true; }
            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());
            return query.executeUpdate() == 1;
        } catch (Exception e) { return false; }
    }
}

四、Service 層委託 DAO

@Service("userService")
public class UserService {
    @Resource(name="userJpaDao")
    private UserJpaDao userJpaDao;

    public User findUserByNameAndPassword(User user) {
        List<User> list = userJpaDao.findByNameAndPassword(user);
        return list.isEmpty() ? null : list.get(0);
    }
    // 其他 CRUD 方法同理委託...
}

五、Jersey Resource 端點

@Component
@Path("/user")
@Scope("prototype")
public class UserResource {
    @Resource(name="userService")
    private UserService userService;

    @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);
        return JSONObject.fromObject(result).toString();
    }

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

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

    @PUT @Path("/updateUser/{id}")
    @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
    public String updateUser(@PathParam("id") Integer id, User user) { /* ... */ }
}

六、Jersey Test 單元測試

public abstract class BaseJerseyServiceTest extends JerseyTest {
    @Override protected WebAppDescriptor configure() {
        return new WebAppDescriptor.Builder("com.spring.jersy.jpa.hibernate.resource")
            .contextParam("contextConfigLocation", "classpath:applicationContext.xml")
            .filterClass(SpringServlet.class)
            .initParam("com.sun.jersey.config.feature.Redirect", "true")
            .initParam("com.sun.jersey.config.feature.FilterForwardOn404", "true")
            .initParam("com.sun.jersey.api.json.POJOMappingFeature", "true")
            .contextListenerClass(ContextLoaderListener.class)
            .requestListenerClass(RequestContextListener.class)
            .build();
    }
}

public class UserResourceTest extends BaseJerseyServiceTest {
    private WebResource webResource;

    @Before public void setUp() { webResource = resource(); }

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

七、測試輸出驗證

Hibernate: insert into test.user (username, password, address, tel) values (?, ?, ?, ?)
{"message":"success"}
Hibernate: select user0_.id as id1_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_... from test.user user0_ where user0_.username=? and user0_.password=?
{"address":"ss","id":48,"password":"1233","tel":"1232333","username":"xx"}

測試通過截圖

八、常見坑:javax.persistence 版本衝突

Caused by: java.lang.NoSuchMethodError: javax.persistence.Table.indexes()[Ljavax/persistence/Index;
    at org.hibernate.cfg.annotations.EntityBinder.processComplementaryTableDefinitions...

原因:類路徑存在多個 JPA API 實現(如 EclipseLink + Hibernate),版本不一致。
解決:在 pom.xml 排除傳遞依賴,統一使用 Hibernate 提供的 hibernate-jpa-2.1-api。

現代化建議:新專案請直接使用 Spring Boot + Spring Data JPA + REST Controller,配置更簡潔、生態更完善;Jersey 可替換為標準 Spring MVC/WebFlux。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved