<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>
<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.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
@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("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 方法同理委託...
}
@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) { /* ... */ }
}
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"}

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。