接到上一篇:Spring Boot基础搭建(二)
重新配置hibernate:
在config包下创建HibernateConfiguration类如下:
代码:
package com.config;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import javax.sql.DataSource;
import java.util.Properties;
/**
* Created by onion on 2017-03-30 15:18.
*/
@Configuration
public class HibernateConfiguration {
//配置sessionFactory
@Bean
public LocalSessionFactoryBean sessionFactory(@Qualifier("dataSource") DataSource dataSource,
@Value("${spring.hibernate.packageScan}") String packageScan,
@Value("${spring.jpa.properties.hibernate.dialect}") String dialect,
@Value("${spring.jpa.show-sql}") String showSql,
@Value("${spring.jpa.properties.hibernate.format_sql}") String formatSql,
@Value("${spring.jpa.properties.hibernate.use_sql_comments}") String useSqlComments,
@Value("${spring.jpa.hibernate.ddl-auto}") String ddlAuto) {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource);
localSessionFactoryBean.setPackagesToScan(packageScan);
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", dialect);
properties.setProperty("hibernate.show_sql", showSql);
properties.setProperty("hibernate.format_sql", formatSql);
properties.setProperty("hibernate.use_sql_comments", useSqlComments);
properties.setProperty("hibernate.hbm2ddl.auto", ddlAuto);
localSessionFactoryBean.setHibernateProperties(properties);
return localSessionFactoryBean;
}
//配置hibernate事务处理
@Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory);
return transactionManager;
}
}
然后在application.properties添加以下代码:
spring.jta.transaction-manager-id=transactionManager
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.hibernate.packageScan=com.tgb.model
将UserService代码修改为:
package com.tgb.service;
import com.tgb.dao.UserDao;
import com.tgb.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* Created by onion on 2017-03-28 09:11.
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {
@Autowired
private UserDao userDao;
public User addUser(User user) {
User user1 = new User();
user1.setName("测试数据");
user1.setAge(2);
userDao.save(user1);
int i = 10/0;
return userDao.save(user);
}
@Transactional(readOnly = true)
public List<User> getAll() {
return userDao.findAll();
}
}
上面的Transactional(rollbackFor = Exception.class)注解是设置事务处理。
在addUser方法中我们int i = 10/0 会出现异常。当出现异常信息时候我们实现了数据回滚。
启动服务访问http://localhost:8080/addUser.html添加一个用户我们可以看到数据库里面没有出现异常前添加的User说明没有问题。
在dao包下新建impl包,如下:
在dao包下新建GenericDao类,如下:
其中代码:
package com.tgb.dao;
import java.util.List;
/**
* Created by onion on 2017-03-30 15:57.
*/
public interface GenericDao<T, PK> {
void save(T entity);
void delete(T entity);
void deleteAll(List<T> entities);
void saveOrUpdate(T entity);
T findById(PK id);
void update(T entity);
List<T> findAll(Object... args);
Object getUniqueObject(String hql, Object... params);
List<T> findAllbyhql(String hql);
void deletebyhql(String hql);
List<T> queryPage(T entity, int page, int rows);
}
在dao.impl下创建类GenericDaoImpl,如下:
代码:
package com.tgb.dao.impl;
import com.tgb.dao.GenericDao;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.HibernateCallback;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
/**
* Created by onion on 2017-03-30 15:59.
*/
public class GenericDaoImpl <T, PK extends Serializable> extends HibernateDaoSupport implements GenericDao<T, PK> {
@Autowired
public void InitSession(SessionFactory sessionFactory) {
setSessionFactory(sessionFactory);
}
private Class<T> clazz;
public GenericDaoImpl() {
clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
@Override
public void save(T entity) {
getHibernateTemplate().save(entity);
}
@Override
public T findById(PK id) {
return getHibernateTemplate().get(clazz, id);
}
@Override
public void update(T entity) {
getHibernateTemplate().update(entity);
}
@Override
public void saveOrUpdate(T entity) {
getHibernateTemplate().saveOrUpdate(entity);
}
@Override
public void delete(T entity) {
getHibernateTemplate().delete(entity);
}
@Override
public void deleteAll(List<T> entities) {
getHibernateTemplate().deleteAll(entities);
}
@Override
public List<T> findAll(Object... args) {
return (List<T>) getHibernateTemplate().find("from " + clazz.getName(), args);
}
@Override
public List<T> queryPage(T entity, int page, int rows) {
return getHibernateTemplate().findByExample(entity, (page - 1) * rows, rows);
}
@Override
public List<T> findAllbyhql(String hql) {
return (List<T>) getHibernateTemplate().find(hql);
}
@Override
public Object getUniqueObject(final String hql, final Object... params) {
Object result = null;
result = getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
Query query = session.createQuery(hql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
query.setParameter(i, params[i]);
}
}
return query.uniqueResult();
}
});
return result;
}
@Override
public void deletebyhql(final String hql) {
getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
session.createQuery(hql).executeUpdate();
return 1;
}
});
}
}
修改UserDao代码如下:
package com.tgb.dao;
import com.tgb.model.User;
/**
* Created by onion on 2017-03-28 09:07.
*/
public interface UserDao extends GenericDao<User,Long> {
}
在dao.impl下创建UserDaoImpl类,如下:
代码:
package com.tgb.dao.impl;
import com.tgb.dao.UserDao;
import com.tgb.model.User;
import org.springframework.stereotype.Repository;
/**
* Created by onion on 2017-03-30 16:03.
*/
@Repository
public class UserDaoImpl extends GenericDaoImpl<User,Long> implements UserDao{
}
在override包下创建LocalNameingStrategy类,如下:
代码:
package com.utils.override;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
/**
* Created by onion on 2017-03-30 16:27.
*/
public class LocalNamingStrategy implements PhysicalNamingStrategy {
@Override
public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
return convert(identifier);
}
@Override
public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
return convert(identifier);
}
@Override
public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
return convert(identifier);
}
@Override
public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
return convert(identifier);
}
@Override
public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
return convert(identifier);
}
private Identifier convert(Identifier identifier) {
if (identifier == null) {
return identifier;
}
String newName = identifier.getText();
return Identifier.toIdentifier(newName);
}
}
这个接口是在服务启动时候hibernate缓存数据库字段前可以调用的接口。newName就是当前的字段名称,可以根据业务去在缓存前修改表字段,比如加前后缀之类的。
然后把它配置到hibernate中。
在HibernateConfiguration中的sessionFactory方法的localSessionFactoryBean.setPackagesToScan(packageScan);
下面添加代码:
localSessionFactoryBean.setPhysicalNamingStrategy(new LocalNamingStrategy());
在override包下新建QueryResInterceptor类如下:
代码:
package com.utils.override;
import org.hibernate.EmptyInterceptor;
/**
* Created by onion on 2017-03-30 16:36.
*/
public class QueryResInterceptor extends EmptyInterceptor {
@Override
public String onPrepareStatement(String sql) {
return super.onPrepareStatement(sql);
}
}
上面的方法是hibernate在执行sql之前会调用的接口,我们可以通过这个方法去动态修改表名称或字段名称,比如每个月给表加上后缀年月份等等,可以实现一些业务上面定期换表的功能。
在HibernateConfiguration中的sessionFactory方法的localSessionFactoryBean.setPackagesToScan(packageScan);
下面添加代码:
localSessionFactoryBean.setEntityInterceptor(new QueryResInterceptor());
可以在刚刚添加到两个类中输出newName和sql。启动和执行sql的时候可以看到配置成功。
GenericDaoImpl实现了使用sessionFactory去执行hql,里面写了一些基础的方法,分页查询可以自己通过拼接hql完成,不会的可以查一下。
。。。。。。。。。。。。。。。。。。。。。。。。。。。
到此hibernate的配置就成功了。
配置jdbctemplate直接执行sql:
hibernate虽然有sql的回调方法,但是在hibernate中写sql总是感觉很奇怪,也比较麻烦,所以我们配置spring jdbc提供给我们的sql执行类去执行sql。
在utils包下创建JdbcUtils,如下:
代码:
package com.utils;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Created by onion on 2017-03-30 16:48.
*/
public class JdbcUtils extends JdbcTemplate {
}
在HibernateConfiguration中添加代码:
@Bean
public JdbcUtils jdbcUtils(DataSource dataSource){
JdbcUtils jdbcUtils = new JdbcUtils();
jdbcUtils.setDataSource(dataSource);
return jdbcUtils;
}
在UserService中添加代码:
@Autowired
private JdbcUtils jdbcUtils;
public void test() {
jdbcUtils.update("INSERT INTO User(name,age) VALUES (?,?)","onion",2);
}
在UserController中添加代码:
@ResponseBody
@RequestMapping("/test")
public String test() {
userService.test();
return "success";
}
启动服务,访问:http://localhost:8080/test可以在数据库看到我们刚刚添加的数据。
删除User表的所有数据。
把UserService中的test方法修改为:
public void test() {
User user1 = new User();
user1.setName("测试数据");
user1.setAge(2);
userDao.save(user1);
int i = 10/0;
jdbcUtils.update("INSERT INTO User(name,age) VALUES (?,?)","onion",2);
}
重新部署访问:http://localhost:8080/test可以看到数据库里面没有出现异常前的数据说明我们在使用相同dataSource的时候会在service层用是同一个链接,所以能够进行事物处理。
到此hibernate以及sql执行的相关配置我们就完成了。
添加统一异常处理:
实现程序运行出错时候能够给前端返回指定的错误信息及格式等等。
在utils包中新建BussinessException类,如下:
代码:
package com.utils;
/**
* Created by onion on 2017-03-30 17:26.
*/
public class BussinessException extends RuntimeException{
public BussinessException(String msg){
super(msg);
}
}
在utils包中新建ExceptionResolver类,如下:
代码:
package com.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* Created by onion on 2017-03-30 17:24.
*/
@ControllerAdvice
public class ExceptionResolver implements HandlerExceptionResolver {
private static ObjectMapper jsonMapper = new ObjectMapper();
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception exception) {
//因为不同的插件请求方式不一样,无法绝对保证该请求是否是ajax请求因此不判断,都统一成ajax请求。
// 判断是否ajax请求
if (!(request.getHeader("accept").indexOf("application/json") > -1 || (request
.getHeader("X-Requested-With") != null && request.getHeader(
"X-Requested-With").indexOf("XMLHttpRequest") > -1))) {
try {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 如果不是ajax,JSP格式返回
//为安全起见,只有业务异常我们对前端可见,否则统一归为系统异常
Map<String, Object> map = new HashMap<String, Object>();
PrintWriter writer = response.getWriter();
map.put("success", false);
map.put("url", request.getRequestURI());
if (exception instanceof BussinessException) {
map.put("errorMsg", exception.getMessage());
} else {
map.put("errorMsg", "系统异常");
}
//这里需要手动将异常打印出来,由于没有配置log,实际生产环境应该打印到log里面
exception.printStackTrace();
writer.write("<!DOCTYPE html>" +
"<html>" +
"<head lang=\"en\">" +
"<meta charset=\"UTF-8\" />" +
"<title>统一异常处理</title>" +
"</head>" +
"<body>" +
"<center><h3>错误路径:" + request.getRequestURL() + "</h3></center>" +
"<center><h3>错误信息:" + map.get("errorMsg") + "</h3></center>" +
"</body>" +
"</html>");
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
} else {
// 如果是ajax请求,JSON格式返回
try {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", false);
exception.printStackTrace();
// 为安全起见,只有业务异常我们对前端可见,否则统一归为系统异常
if (exception instanceof BussinessException) {
map.put("errorMsg", exception.getMessage());
} else {
map.put("errorMsg", "系统异常!");
}
writer.write(jsonMapper.writeValueAsString(map));
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
上面的异常类是用来达到如果是我们自定义的异常(业务异常)就输出我们自定义的异常信息反之都归为系统异常。
启动服务访问:http://localhost:8080/test可以看到我们的设置已经起效。
添加AOP完成日志记录:
在utils包下创建LogAdvice类,如下:
代码:
package com.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* Created by onion on 2017-03-30 21:13.
*/
@Aspect
@Component
public class LogAdvice {
private final static ObjectMapper jsonMapper = new ObjectMapper();
private static final Logger logger = Logger.getLogger(LogAdvice.class
.getName());
@Pointcut("execution(* com.tgb.service.*.*(..))")
public void show() {
}
@Before("show()")
public void Befor(JoinPoint jp) {
try {
logger.info("调用方法前[" + jp.toLongString() + "] 传入相关数据:" + jsonMapper.writeValueAsString(jp.getArgs()));
} catch (Exception e) {
e.printStackTrace();
}
}
@AfterThrowing(value = "show()", throwing = "e1")
public void afterThrowing(JoinPoint jp, Exception e1) {
try {
logger.error("(错误)调用方法后[" + jp.toLongString() + "] 错误信息:" + e1.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
@AfterReturning(value = "show()", returning = "obj")
public Object afterReturning(JoinPoint jp, Object obj) {
try {
logger.info("调用方法后[" + jp.toLongString() + "] 返回相关数据:" + jsonMapper.writeValueAsString(obj));
} catch (Exception e) {
e.printStackTrace();
logger.error("LogAfter:" + e.getMessage());
}
return null;
}
}
分别为执行前,执行错误后和执行成功后打出日志。
接下来配置日志输出到文件,在application.properties中添加:
logging.file=E://logs/log.log
因为spring boot里面包含log4j所以在设置输出目录后就可以看到我们的日志文件。
到此spring boot的基础搭建基本完成。
代码下载:http://download.csdn.net/detail/qq_36224522/9799483
文档下载:http://download.csdn.net/detail/qq_36224522/9799487