我的Web服务写入两个MySQL表(一个接一个;依赖外键)。我已经使我的服务方法[upload(..)]抛出强制异常只是为了检查回滚功能。即使抛出异常,记录也会保存在file_store表(第一个表)中。请帮我弄清楚什么是错的。另外,如果任何上下文配置不正确,请告诉我。感谢。
文件:root-context.xml
<?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:jpa="http://www.springframework.org/schema/data/jpa"
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/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- Database -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/app?autoReconnect=true"/>
<property name="username" value="${datasource.username}"/>
<property name="password" value="${datasource.password}"/>
</bean>
<!-- JPA Vendor Adapter -->
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
</bean>
<!-- Entity Manager Factory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="packagesToScan" value="com.app.test.persistence" />
</bean>
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- Detect @Transactional -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<!-- JPA Repositories -->
<jpa:repositories base-package="com.app.test.repository"/>
<!-- JASYPT Configuration -->
<bean id="configurationEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="algorithm">
<value>PBEWithMD5AndDES</value>
</property>
<property name="password">
<value>com.app.test</value>
</property>
</bean>
<bean id="propertyConfigurer" class="org.jasypt.spring.properties.EncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="configurationEncryptor" />
<property name="locations">
<list>
<value>classpath:runtime.properties</value>
</list>
</property>
</bean>
<!-- Properties Util -->
<bean id="propertiesUtil" class="com.app.test.util.PropertiesUtil">
<property name="location" value="classpath:app.properties"></property>
</bean>
</beans>
文件:servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
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.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Detect @Controller -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/**" location="/WEB-INF/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.app.test" />
<beans:bean id="jsonMapper" class="com.fasterxml.jackson.databind.ObjectMapper"></beans:bean>
</beans:beans>
文件:web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
文件:File.Java(Entity-1)
@Entity
@Table(name="files")
@NamedQuery(name="File.findAll", query="SELECT f FROM File f")
public class File implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
@Lob
private byte[] content;
//bi-directional many-to-one association to FileStore
@ManyToOne
@JoinColumn(name="file_store_key")
private FileStore fileStore;
public File() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public byte[] getContent() {
return this.content;
}
public void setContent(byte[] content) {
this.content = content;
}
public FileStore getFileStore() {
return this.fileStore;
}
public void setFileStore(FileStore fileStore) {
this.fileStore = fileStore;
}
}
文件:FileStore.Java(Entity-2)
@Entity
@Table(name="file_store")
@NamedQuery(name="FileStore.findAll", query="SELECT f FROM FileStore f")
public class FileStore implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="unique_key")
private String uniqueKey;
private String checksum;
@Column(name="uploaded_from")
private String uploadedFrom;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="uploaded_on")
private Date uploadedOn;
//bi-directional many-to-one association to File
@OneToMany(mappedBy="fileStore")
private List<File> files;
public FileStore() {
}
public String getUniqueKey() {
return this.uniqueKey;
}
public void setUniqueKey(String uniqueKey) {
this.uniqueKey = uniqueKey;
}
public String getChecksum() {
return this.checksum;
}
public void setChecksum(String checksum) {
this.checksum = checksum;
}
public String getUploadedFrom() {
return this.uploadedFrom;
}
public void setUploadedFrom(String uploadedFrom) {
this.uploadedFrom = uploadedFrom;
}
public Date getUploadedOn() {
return this.uploadedOn;
}
public void setUploadedOn(Date uploadedOn) {
this.uploadedOn = uploadedOn;
}
public List<File> getFiles() {
if (null == this.files)
this.files = new ArrayList<File>();
return this.files;
}
public void setFiles(List<File> files) {
this.files = files;
}
public File addFile(File file) {
getFiles().add(file);
file.setFileStore(this);
return file;
}
public File removeFile(File file) {
getFiles().remove(file);
file.setFileStore(null);
return file;
}
}
文件:FileStoreDAOImpl.java
package com.app.test.dao.impl;
@Repository
public class FileStoreDAOImpl implements FileStoreDAO {
private static final Logger LOGGER = LoggerFactory.getLogger(FileStoreDAOImpl.class);
@Autowired
private FileStoreRepo fileStoreRepo;
@Override
public FileStore saveFileStore(FileStore fStore) throws Exception {
LOGGER.info("Inside saveFileStore");
try {
fileStoreRepo.saveAndFlush(fStore);
return fStore;
} catch (Exception e) {
LOGGER.error("Exception occurred while saving file store: " + e.getMessage());
throw e;
}
}
}
文件:FileStoreServiceImpl.java
package com.app.test.service.impl;
@Service
@Transactional
public class FileStoreServiceImpl implements FileStoreService {
@Autowired
private FileStoreDAO fileStoreDAO;
@Autowired
private FileDAO fileDAO;
private static final Integer ID_LENGTH = 16;
@Override
@Transactional(rollbackFor={Exception.class, RuntimeException.class})
public String upload(UploadRequest jRequest, String uploadedFrom) throws Exception {
//String fileKey = null;
try {
//get the file content from packet
String fileContent = jRequest.getContent();
//compute checksum
String checksum = DigestUtils.sha1Hex(fileContent);
//check if similar file exists
if (!fileStoreDAO.checksumExists(checksum)) {
//create file store object
FileStore fStore = new FileStore();
fStore.setUniqueKey(RandomUtil.getRandomKey(checksum, ID_LENGTH));
fStore.setChecksum(checksum);
fStore.setUploadedOn(new Date());
fStore.setUploadedFrom(uploadedFrom);
fileStoreDAO.saveFileStore(fStore);
//create file object
File file = new File();
file.setFileStore(fStore);
file.setContent(fileContent.getBytes());
throw new Exception("Forced exception"); //Expecting Spring JPA to rollback the transaction; but not happenning :(
/*fileDAO.saveFile(file);
} else {
throw new Exception("Similar file already exists.");
}
} catch (Exception e) {
throw e;
}
//return fileKey;
}
}
文件:FileController.java
package com.app.test.controller;
@Controller
public class FileStoreController {
@Autowired
FileStoreService fileStoreService;
@Autowired
ObjectMapper jsonMapper;
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView processIndex(HttpServletRequest request, HttpServletResponse response) {
return new ModelAndView("index", null);
}
private String retrieveData(HttpServletRequest request) {
StringBuffer jBuffer = new StringBuffer();
String line = null;
try {
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null)
jBuffer.append(line);
} catch (Exception e) {
return null;
}
return jBuffer.toString();
}
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public String processUpload(HttpServletRequest request, HttpServletResponse response) {
UploadResponse jResponse = new UploadResponse();
try {
//retrieve request packet
String pData = retrieveData(request);
String fromAddress = request.getRemoteAddr();
if (null != pData && !("".equals(pData))) {
UploadRequest jRequest = jsonMapper.readValue(pData, UploadRequest.class);
String fileKey = fileStoreService.upload(jRequest, fromAddress);
UploadSuccess success = new UploadSuccess();
success.setFileId(fileKey);
success.setMessage("File uploaded successfully");
jResponse.setSuccess(success);
} else {
Error error = new Error();
error.setMessage("Packet is empty.");
jResponse.setError(error);
}
} catch (Exception e) {
Error error = new Error();
error.setMessage(e.getMessage());
jResponse.setError(error);
}
try {
return jsonMapper.writeValueAsString(jResponse);
} catch (Exception ex) {
return "Fatal exception occurred while processing upload request.";
}
}
}
答案 0 :(得分:2)
您必须在根上下文文件上扫描您的服务,该文件是您的applicationContext但不在servlet上,请参阅此文件以了解有关differences
的更多信息扫描root-context
上的服务:
<context:component-scan
base-package="com.app.test.service.impl"/>
配置servlet-context
,以便您只扫描控制器以避免重复的类:
<context:component-scan
base-package="com.app.test.controller"/>
默认情况下,spring-transactionnal将回滚RuntimeException
,不需要在回滚类列表中添加它们:
@Transactional(rollbackFor=..)
修改强> Spring的事务管理回滚事务仅针对未经检查的异常(RuntimeException)
throw new RuntimeException("forced exception");