一、数据库准备
1.创建数据库maven
create database maven character set utf8 collate utf8_general_ci; //use maven;2.创建用户表
create table t_users(
id int primary key auto_increment, username varchar(30) not null, password varchar(50) not null, constraint unq_users_username unique(username) );3.插入测试数据
insert into t_users(username,password) values('admin', md5('admin'));
insert into t_users(username,password) values('user', md5('user'));二、项目框架构建1.创建简单父工程maven-parent,注意packaging类型为pom
2.复制内容以引入jar包 3.创建子工程(Maven Module)maven-entity,注意packaging类型为jar,选择父类型为maven-parent 4.创建子工程(Maven Module)maven-utils,注意packaging类型为jar,选择父类型为maven-parent 5.创建子工程(Maven Module)maven-dao,注意packaging类型为jar,选择父类型为maven-parent 6.创建子工程(Maven Module)maven-service,注意packaging类型为jar,选择父类型为maven-parent 7.创建子工程(Maven Module)maven-web,,注意packaging类型为war,选择父类型为maven-parent 8.将maven-parent发布到本地仓库,maven会自动将所有子工程发布到本地仓库 9.按顺序为各个子项目添加依赖关系,具体依赖顺序如下 web--->service-->dao--->utils--->entity 10.在子工程之间添加依赖的具体操作如下: 10.1 发布工程后,最好先打开Maven Repository重建一下索引 10.2 打开maven-utils子工程的porm.xml,选择dependencies标签添加对maven-entity的依赖(也可以直接编辑porm.xml源码添加) 10.3 打开maven-dao子工程的porm.xml,选择dependencies标签添加对maven-utils的依赖(也可以直接编辑porm.xml源码添加) 10.4 打开maven-service子工程的porm.xml,选择dependencies标签添加对maven-dao的依赖(也可以直接编辑porm.xml源码添加) 10.5 打开maven-web子工程的porm.xml,选择dependencies标签添加对maven-web的依赖(也可以直接编辑porm.xml源码添加) 11.子工程之间的依赖会以工程的形式出现在lib中,若关闭某一子工程,则会变为以jar方式引入 12.在maven-web工程的webapp创建WEB-INF目录及web.xml文件三、构建maven-entity子工程1.在src/main/java的com.hao.entity下编写User.java,并采用Hibernate注解映射实体
package com.hao.entity;// Generated 2017-8-6 12:57:28 by Hibernate Tools 4.0.0
import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import static javax.persistence.GenerationType.IDENTITY; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.UniqueConstraint;/**
* TUsers generated by hbm2java */ @Entity @Table(name = "t_users", catalog = "maven", uniqueConstraints = @UniqueConstraint(columnNames = "username")) public class User implements java.io.Serializable {private static final long serialVersionUID = 1L;
private Integer id;
private String username; private String password;public User() {
}public User(String username, String password) {
this.username = username; this.password = password; }@Id
@GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) public Integer getId() { return this.id; }public void setId(Integer id) {
this.id = id; }@Column(name = "username", unique = true, nullable = false, length = 30)
public String getUsername() { return this.username; }public void setUsername(String username) {
this.username = username; }@Column(name = "password", nullable = false, length = 50)
public String getPassword() { return this.password; }public void setPassword(String password) {
this.password = password; }}
四、构建maven-utils子工程1.在src/main/java下编写MD5工具类,用于密码加密
package com.hao.utils;
import java.math.BigInteger;
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException;public class MD5Utils {
/** * 使用md5的算法进行加密 */ public static String md5(String plainText) { byte[] secretBytes = null; try { secretBytes = MessageDigest.getInstance("md5").digest( plainText.getBytes()); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("没有md5这个算法!"); } String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字 // 如果生成数字未满32位,需要前面补0 for (int i = 0; i < 32 - md5code.length(); i++) { md5code = "0" + md5code; } return md5code; }public static void main(String[] args) {
System.out.println(md5("123")); }}
五、构建maven-dao子工程1.在src/main/java下进行BaseDao的抽取以及UserDao代码的编写
1.1 BaseDao接口 package com.hao.dao.base;import java.io.Serializable;
import java.util.List;public interface BaseDao<T> {
void save(T entity);
void delete(T entity);
void deleteById(Serializable id);
void update(T entity);
T findById(Serializable id);
List<T> findAll();
} 1.2 BaseDaoImpl实现类 package com.hao.dao.base.impl;import java.io.Serializable;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List;import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate5.support.HibernateDaoSupport;import com.hao.dao.base.BaseDao;
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> {
private Class<T> entityClass;
@SuppressWarnings("unchecked")
public BaseDaoImpl() {// 获取子类对象的父类类型
ParameterizedType superClass = (ParameterizedType) this.getClass() .getGenericSuperclass(); // 获得在父类类型上声明的反省数组 Type[] genericTypes = superClass.getActualTypeArguments(); // 第一个泛型即为实体类型 entityClass = (Class<T>) genericTypes[0]; }@Override
public void save(T entity) { getHibernateTemplate().save(entity); }@Override
public void delete(T entity) { getHibernateTemplate().delete(entity); }@Override
public void deleteById(Serializable id) { T entity = getHibernateTemplate().load(entityClass, id); getHibernateTemplate().delete(entity); }@Override
public void update(T entity) { getHibernateTemplate().update(entity); }@Override
public T findById(Serializable id) { return getHibernateTemplate().get(entityClass, id); }@Override
public List<T> findAll() { return getHibernateTemplate().loadAll(entityClass); }/**
* HibernateDao接口在使用前必须注入SessionFactory * * @param sessionFactory */ @Autowired public void setSF(SessionFactory sessionFactory) { super.setSessionFactory(sessionFactory); } } 1.3 UserDao接口 package com.hao.dao;import com.hao.dao.base.BaseDao;
import com.hao.entity.User;public interface UserDao extends BaseDao<User> {
User login(String username, String password);
} 1.4 UserDaoImpl实现类 package com.hao.dao.impl;import java.util.List;
import org.springframework.stereotype.Repository;
import com.hao.dao.UserDao;
import com.hao.dao.base.impl.BaseDaoImpl; import com.hao.entity.User;@Repository("userDao")
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {@Override
public User login(String username, String password) {@SuppressWarnings("unchecked")
List<User> user = (List<User>) getHibernateTemplate().find( "from User u where u.username=? and u.password=?", username, password);if (user == null || user.size() < 1) {
return null; } else { return user.get(0); } } }2.在src/main/resources下创建applicationContext-dao.xml文件,编写属于dao层的内容
<!-- 开启扫描注解Bean --> <context:component-scan base-package="com.hao" /><context:property-placeholder location="classpath:db.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}" /> <property name="jdbcUrl" value="${jdbc.jdbcUrl}" /> <property name="user" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </bean><!-- 加载配置方案2:在spring配置中放置hibernate配置信息 -->
<bean name="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <!-- 将连接池注入到sessionFactory, hibernate会通过连接池获得连接 --> <property name="dataSource" ref="dataSource"></property> <!-- 配置hibernate基本信息 --> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect </prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop></props>
</property> <!-- 引入orm元数据,指定orm元数据所在的包路径,spring会自动读取包中的所有配置 --> <property name="packagesToScan" value="com.hao.entity"></property> </bean> 3.在src/main/resources下创建db.properties jdbc.jdbcUrl=jdbc:mysql://localhost:3306/maven jdbc.driverClass=com.mysql.jdbc.Driver jdbc.user=root jdbc.password=h66666 4.在src/main/resources下创建log4j.properties log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%nlog4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=D:\\temp\\mylog.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n### fatal error warn info debug debug trace
log4j.rootLogger=debug, stdout #log4j.logger.org.hibernate=INFO #log4j.logger.org.hibernate.type=INFO 5.为了在Dao层能对save,update之类的方法进行测试,还需而外提供Service层中的Spring声明式事务配置 6.故在src/test/resources下创建applicationContext-daotest.xml,提供和事务相关的配置(最好与applicationContext-service中的事务配置保持一致) <bean name="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager" > <property name="sessionFactory" ref="sessionFactory"/> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> 7.执行单元测试时,实际上需要使用到多个applicationContext文件,其中applicationContext-daotest.xml就是在src/test/resources中定义的 8.单元测试依赖多个applicationContext文件时,要在@ContextConfiguration的locations中列举出来 9.在src/test/java的com.hao.dao包下编写Dao层单元测试代码,注意要添加@Transaction注解 package com.hao.dao;import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional;import com.hao.entity.User;
import com.hao.utils.MD5Utils;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-dao.xml", "classpath:applicationContext-daotest.xml" }) @Transactional public class UserDaoTest {@Autowired
UserDao userDao;@Test
public void testLogin() {Assert.assertNotNull(userDao.login("admin", MD5Utils.md5("admin")));
Assert.assertNull(userDao.login("admin", MD5Utils.md5("pass"))); Assert.assertNotNull(userDao.login("user", MD5Utils.md5("user")));System.out.println(userDao.login("admin", MD5Utils.md5("admin")));
}@Test
public void testSave() { User u = new User(); u.setUsername("dao"); u.setPassword("dao"); userDao.save(u); }}
六、构建maven-service子工程1.在src/main/java下编写UserService相关代码
1.1 UserService接口 package com.hao.service;import com.hao.entity.User;
public interface UserService {
User login(User user);
void save(User user);
}
1.2 UserServiceImpl实现类 package com.hao.service.impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional;import com.hao.dao.UserDao;
import com.hao.entity.User; import com.hao.service.UserService; import com.hao.utils.MD5Utils;@Service("userService")
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, readOnly = true) public class UserServiceImpl implements UserService {private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) { this.userDao = userDao; }@Override
public User login(User user) { String pass = MD5Utils.md5(user.getPassword()); return userDao.login(user.getUsername(), pass); }@Override
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, readOnly = false) public void save(User user) { userDao.save(user); } } 2.在src/main/resources下创建applicationContext-service.xml,编写service层相关的配置(主要是声明式事务的配置) <!-- 定义核心事务管理器 --> <bean name="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager" > <property name="sessionFactory" ref="sessionFactory"/> </bean> <!-- 开启注解事务 --> <tx:annotation-driven transaction-manager="transactionManager" /> 3.多个applicationContext也可以采用通配符方式配置,如"classpath*:applicationContext-*.xml",但不建议 4.因为使用通配符方式时,我在eclipse能成功执行单元测试,但使用maven的install命令发布到本地仓库时,单元测试代码会执行失败 5.出错原因为为dao子工程的applicationContext找不到,因此建议使用列举文件方式而不要采用通配符的方式 6.注意,第一个通配符表示读取包括类路径和jar包下的径的配置文件 7.在对Service进行单元测试时,可添加@Transactional避免脏数据的产生,否则会使用service方法中配置的事务,会产生脏数据 8.在src/test/java下编写Service层的单元测试代码 package com.hao.service;import junit.framework.Assert;
import org.junit.Test;
import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional;import com.hao.entity.User;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:applicationContext-dao.xml", "classpath:applicationContext-service.xml"}) @Transactional public class UserServiceTest {@Autowired
UserService userService;@Test
public void testLogin() {User u1 = new User();
u1.setUsername("admin"); u1.setPassword("admin");User u2 = new User();
u2.setUsername("admin"); u2.setPassword("pass");User u3 = new User();
u3.setUsername("user"); u3.setPassword("user");Assert.assertNotNull(userService.login(u1));
Assert.assertNull(userService.login(u2)); Assert.assertNotNull(userService.login(u3)); }@Test
public void testSave() {User u = new User();
u.setUsername("service"); u.setPassword("service");userService.save(u);
}
}
七、构建maven-web子工程1.action的抽取以及UserAction的编写
1.1 抽取BaseAction package com.hao.action.base;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.util.Map;import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;public class BaseAction<T> extends ActionSupport implements ModelDriven<T>,
RequestAware, SessionAware {private static final long serialVersionUID = 1L;
protected Map<String, Object> request;
protected Map<String, Object> session; protected T model;@Override
public void setRequest(Map<String, Object> request) { this.request = request; }@Override
public void setSession(Map<String, Object> session) { this.session = session; }@Override
public T getModel() { return model; }public BaseAction() {
// 获取父类
ParameterizedType genericSuperclass = (ParameterizedType) this .getClass().getGenericSuperclass(); // 获取父类的泛型数组 Type[] types = genericSuperclass.getActualTypeArguments(); // 取得第一个泛型,即Model的类型 @SuppressWarnings("unchecked") Class<T> entityClass = (Class<T>) types[0]; try { model = entityClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); throw new RuntimeException(e); } }}
1.2 UserAction代码编写,注意scope要为prototype package com.hao.action;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller;import com.hao.action.base.BaseAction;
import com.hao.entity.User; import com.hao.service.UserService;@Controller("userAction")
@Scope("prototype") public class UserAction extends BaseAction<User> {private static final long serialVersionUID = 1L;
public String login() {
System.out.println("-------------------------------------------------------" + this + "-------------------------------------------------------"); User user = userService.login(model); if (user == null) { request.put("errorInfo", "用户名或密码错误"); return LOGIN; } session.put("loginUser", user); return SUCCESS; }@Autowired
private UserService userService;public UserService getUserService() {
return userService; }public void setUserService(UserService userService) {
this.userService = userService; }}
2.在struts.xml中配置struts,整合spring <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"><struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <constant name="struts.objectFactory" value="spring" /><package name="hao" namespace="/" extends="struts-default">
<action name="userAction_*" class="userAction" method="{1}"> <result>/success.jsp</result> <result name="login">/index.jsp</result> </action> </package></struts>
3.在web.xml中配置spring监听器,让spring容器随web项目启动而启动 <!-- 配置spring框架的监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext-*.xml</param-value> </context-param> 4.配置过滤器,用于扩展Hibernate Session的作用域知道请求结束,注意一定要配置在struts2过滤器之前 <!-- 配置过滤器,解决hibernate延迟加载问题 --> <filter> <filter-name>openSessionInView</filter-name> <filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>openSessionInView</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 5.配置struts2过滤器 <!-- 配置struts2的过滤器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 八、编写页面,启动项目1.index.jsp,注意引入jstl标签库
<form action="userAction_login.action" method="post">
<span>用户名:</span><input type="text" name="username"/><br/> <span>密 码:</span><input type="password" name="password"/><br/> <input type="submit" value="提交"/> </form> <c:if test="${not empty requestScope.errorInfo }"> <c:out value="${errorInfo }"/> </c:if> 2.success.jsp <h5>${sessionScope.loginUser.username }登陆成功</h5><hr/> 3.使用tomcat7:run命令运行或者使用eclipse从执行服务器运行,访问index.jsp测试即可