因学的东西还给老师,所以现如今又重新学习一遍.
工作需要配置一个框架 SSH嘛,好吧又重新看资料,从头配置
不过版本多样性搞得我眼花缭乱,网上什么都有,每处都只能看懂一部分,finally 结合在一起.
要说复习那就得按部就班,不能一手掌握
工具
- MyEclipse 2014 GA pro
- MySQL 5.6.9
- Hibernate 4.1
- Spring 3.1
- Struts 2.1
Hibernate
花了一天时间看够了hibernate,连上了mysql,但不仅仅满足于此
不能理解,下次又得忘.
操作记录:
1.创建一个项目 ssh (Java WebProject)
web3.1
javaversion 1.7
jstl 默认
最后我选择了3.1和1.7图片上是3.0和 1.6
这时候webroot 和 src下面都没有 xml文件
2.MyEclipse2014 添加框架就是不一样(接触其他版本少)
添加 sturts
右键项目->MyEclipse->Project Facets
下面选择 struts2.x 弹出界面呢 第一个界面不动
第二个界面修改为 /* 然后
第三个界面只够选了Core 没有勾选Spring Plugin
原因是添加Spring会有此包
finish
然后,又多了几个东西
src下面多了个struts.xml
WEB-INF下面多了个web.xml
web.xml里面配置了struts.xml
<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>
这里也就可以改以后action的访问方式了
而 struts.xml里面 ..却是一片空白,
这个 交给之后添加
添加spring
非要我说 谁先谁后 额是名字问题吧(开玩笑)
还是版本在搞怪,这就不说 管他先拿筷子还是先拿碗能吃就行
右键项目->MyEclipse->Project Facets
下面选择 spring ,弹出界面
版本选择3.1 我最高就3.1既然是学习就学最新的吧.
然后 next ,如图
一开始看别人说的步骤 要去掉什么要选择什么特别纠结,那就查呗
Enable AOP Builder 选与不选 在于项目大小 增加耦合度的作用
大型项目翻译上说可以提高性能无所谓了
file : applicationContext.xml这个文件位置可以之后更改,也可以不更改
看需不需要集中管理(分类)
然后嘛 finish就是 ,对于经验不足悟性不够的我们来说,完全明白自己需要哪个包是困难的,使用xml配置 还是困难
这次又多了一些东西
WEB-INF目录下面
web.xml:
<!-- 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>
<!-- Spring -->
然后把 <param-value>classpath:applicationContext.xml</param-value>
改为 <param-value>/WEB-INF/applicationContext*.xml,classpath*:applicationContext*.xml</param-value>
默认运行位置为 web-info/classes/app….xml,改了后,可以放在web-info目录,也可以放在 web-info/classes/自定义目录 , 自定义目录下名字以applicationContext开头的即可.
还有两个tld文件 这个就不多说
Src 目录
多了一个 ApplicationContext.xml
内容 除了申明也没有其他内容,这得配置事务等等的时候才用
添加hibernate
步骤:
建一个 根包 比如 app,
- 然后建一个子包比如模块名比如 新闻模块 news
- 然后分别建三个子包 dao , service , controller 即为三层
- 另外在添加一个包 table 用于存放表的实体类 跟上面的三层同级或其他位置
- dao 存放 操作数据库的文件
为什么叫要叫 dao , DAO是Data Access Object 意思意思总之是
存放跟数据库打交道的东西
- 连接数据库
为了方便新建一个数据库连接用于可视化操作,可供多个项目使用
菜单栏 Window->Open Perspective->My Eclipse Database Explorer
右键 new 如图
用了MySql 自然选它,也不知道谁会全部用过,
- Drver name 别名
- connection url 连接字符串,有些要写数据库名能写就写
- user name ,password
- Driver jARs 驱动jar包,这个网上有,最好跟你数据库版本匹配
- test Driver 测试连接成功与否 success为成功
- save password 保存密码
finish ok
然后添加hibernate
右键项目->MyEclipse->Project Facets
下面选择 hibernate ,弹出界面
选择了我最高版本4.1 next,
不创建hibernate.cfg.xml和sessionFactory.class
整合可以不需要,
测试 的的话有需要勾上
next 如图
这里的Specify Spring DataSource connection details?
问的是需不需要在spring的xml 也就是applicationcontext.xml添加
sessionFactory 映射
选择刚才添加的数据库连接
enable dynamic DB table creation 是否启用动态创建数据表功能.不需要就不点 ,然后finish,弹出是否更换操作界面 随便勒.
现在 添加hibernate之后 多了哪些内容呢:
applicationContext.xml:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
以上是原版XML配置,之后在做详细修改
一个sessionFactory 的bean 和一个
transactionManager注解需要用到
src目录没有hibernate.cfg.xml,因为我没创建
hibernate 实体表
两种:(我所知)
一种用 注解 在类里面用注解(@xxx的样式)就不需要xml了
一种用 映射 用xml 写属性,不过这种方式会产生大量的配置文件
我选择了注解的方式.
步骤:
1.取保MYSQL服务开启.
2.打开database explorer界面(window-show-view - DB-browser)
3.选择表,右键hibernate reverse
4.在弹出的界面中设置
5.next 简单设置
第二个界面,若是多个表有关系的需要使用到,这里是一个表就不管他了,
有经验了在尝试.
此处可以设置的内容: enable many to many one to one 即表间关系
多个表需要勾选
6.next第三个界面 详细设置
- 表名设置和主键设置
- 字段设置,最好不要改名字,类型我们可以之后再改
finish.
生成了一个实体类,这时的实体类已经可以用
HibernateSessionFactory.getSession().get(User.class,1)查询到记录了
若之前勾选了创建HibernateSessionFactory的话.
get(实体类.class,表的主键) 1 会被封装成Integer对象
@Entity
@Table(name = "user", catalog = "news") //name表名,catalog 数据库名
public class User implements java.io.Serializable {
// Fields
private Integer userId;
private String username;
private String password;
若数据类型不对这里修改
applicationcontext.xml里面多了一些内容
<property name="annotatedClasses">
<list>
<value>app.news.table.User</value></list>
</property>
这只是暂时的,我们修改为另一种,自动扫描,之后就不用在
数据表逆向工程,添加的时候勾选
update hibernate configuration with mapping resource location了
将上代码修改为:
<!--自动扫描实体:app.news.table包下的类 -->
<property name="packagesToScan" value="app.news.table" />
多个表生成实体类,就有点高级了,那关系堪比乱==伦
理不清就惨,不过还好可以一次次尝试
一个个生成,不对就勾选其他选项.哈哈
我创建了两个表
一个 User 表
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(25) NOT NULL,
`password` varchar(25) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
一个UserInfo表
CREATE TABLE `userinfo` (
`userinfo_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`sex` varchar(10) DEFAULT '男' COMMENT '性别',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`userinfo_id`),
UNIQUE KEY `user_id` (`user_id`),
KEY `userinfo_ibfk_1` (`user_id`),
CONSTRAINT `userinfo_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`User_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
可能逻辑上有问题,只为了测试一种可能性
userinfo表有一个字段是 user_id(外键),对应user表的主键,
userinfo.user_id 设置了两个约束,一个唯一约束,一个外键约束
还有级联,也就是userinfo(从表)user(主表)
主表删除记录,对应的从表中的记录会被删除.
一般来说,从表不应该有没有外键的记录,看需要设置为非空.
好,如下图步骤
1.假若有两张表右键创建
2.选择POJO,下一步
applicationContext.xml 又修改成这样了所以替换成自动扫描
<property name="annotatedClasses">
<list>
<value>app.news.table.User</value>
<value>app.news.table.UserInfo</value>
</list>
</property>
介样子:
<!--自动扫描实体:app.news.table包下的类 -->
<property name="packagesToScan" value="app.news.table" />
若需要测试hibernate,hibernate里面需要设置成
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 基本配置:JDBC方式 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 扩展配置 -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.jdbc.fetch_size">100</property>
<property name="hibernate.jdbc.batch_size">30</property>
<!-- 领域对象:Annotation注解方式 -->
<mapping class="app.news.table.User" />
<mapping class="app.news.table.UserInfo" />
</session-factory>
</hibernate-configuration>
领域对象 Annotation 注解方式那儿就是 对应的刚创建的两个类
而两个类中的代码有些不是我所想要的结果,所以要修改
User.java
@Table(name = "user", catalog = "news") //catalog就是数据库名
public class User implements java.io.Serializable {
// 字段
private Integer userId;
private String username;
private String password;
private Set<UserInfo> userInfos = new HashSet<UserInfo>(0);
//User需不需要持有userinfo看需求,按逻辑上来说,主表是不需要持有从表的,所以这儿不应该有userinfos
//它的get方法 一对多,即一个用户账户对应多个用户信息,一对一是一对多的一个特例,之后咱就不用他了
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user")
public Set<UserInfo> getUserInfos() {
然后是 UserInfo.java
@Table(name = "userinfo", catalog = "news", uniqueConstraints = @UniqueConstraint(columnNames = "user_id"))//好像这就是外键声明哈
public class UserInfo implements java.io.Serializable {
// 字段
private Integer userinfoId;
private User user;
//若不需要联合查找这儿只该为Integer user_id,不过为了方便设置为一个对象不错
private String sex;
private String name;
//User 的 get方法,多对一,即多个用户信息对一个用户账户,哈,账户是唯一的,之后修改看变成啥.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", unique = true)
public User getUser() {
同时选中两个表创建的POJO对象,是双向一对多关系,我只需要单向关系
即 userinfo 持有 user对象,而user不需要持有userinfo对象,因为user是主表,没有外键,Userinfo 的user_id是外键,设置了唯一性约束,外键约束,所以可以为 OneToOne的关系.
此时我们需要做的是去掉user持有的userinfo对象,和修改userinfo的对应关系.步骤图如下:
1.右键user表,选择hibernate Reverse Engineering ,弹出的界面中设置
不用勾选Update Hibernate configuration…,因之前applicationcontext.xml设置了扫描pojo包的方式,不用单个配置.
2.只设置types
3.什么都不勾选.
User.java :
@Table(name = "user", catalog = "news")
public class User implements java.io.Serializable {
// 字段
private Integer userId;
private String username;
private String password;
不再持有userinfo对象 独立了.
然后修改或者不修改userinfo
UserInfo.java :
@Table(name = "userinfo", catalog = "news", uniqueConstraints = @UniqueConstraint(columnNames = "user_id"))
public class UserInfo implements java.io.Serializable {
// 字段
private Integer userinfoId;
private User user;
private String sex;
private String name;
//User.user的get方法
@ManyToOne(fetch = FetchType.LAZY)
//如果没有将user_id设置为唯一约束,这里就是ManyToOne,
//而如果你设置了user_id为唯一约束,这里就应该显示的设置为OneToOne
//虽然结果都一样,唯一约束修改为@OneToOne(fetch = FetchType.LAZY)
//@OneToOne()里面fetch,为加载自身对象时是否加载引用的对象,
//LAZY为需要的时候加载,还有一些属性值得一提的是cascade = CascadeType.ALL
//即@OneToOne(fetch= FethchType.LAZY,cascade = CascadeType.ALL)
//作用是级联操作,所有操作都是级联,详细百度@onetoone
//更改后ctrl+shift+o,自动引用删除需要/不需要的包
@JoinColumn(name = "user_id", unique = true)
public User getUser() {
return this.user;
}
到了这里我才明白之前的一些问题
这里下面可以定义实体类
Include referenced tables (A->B)
同时生成这个表引用的另外一个表的实体类(java文件)
Include referenced tables (A<-B)
同时生成引用这个表的另外一个表的实体类(java文件)
Generate support for ListedTable(fk)->UnlistedTable:
如果当前表格是一个从表,生成当前从表实体类到主表实体类访问
Generate support for UnlistedTable(fk)<- ListedTable:
如果当前实体类是一个主表,生成当前实体类到从表实体类访问
以上是第三个界面的选项, A->B和 A<-B好理解
前者就是在右键table中单个表创建POJO的时候,不仅创建自己,还要创建一个引用的表的实体类. 自己(从表),引用的表(主表),userinfo和user
后者,是创建user(主表)时,并创建一个引用自己的表的实体类(userinfo.java)
最下面的两个选项,不知道是我这两个表体现不出效果的原因,还是怎么的.
不管钩不勾选只要勾选了上面A<-B或 A->B,都没区别.
然后是测试:
首先,我们使用HibernateSessionFactory.java文件测试,其他的麻烦不会.
还有必要文件.hibernate.cfg.xml
上面有文件代码,最关键的是有这两个表的映射
<!-- 领域对象:Annotation注解方式 -->
<mapping class="app.news.table.User" />
<mapping class="app.news.table.UserInfo" />
不然会找不到表.
关于这两个文件的创建:
右键项目 选择new,(若列表中有Hibernate sessionFactory,和Hibernate Configuration File直接选择)没有选择 other,在MyEclipse 下面的Hibernate中有这两个选项.
创建的xml没有映射,所以需要将上面的代码粘贴
Test.java 测试 :
在hibernate.cfg.xml session-factory 标签内配置一个属性
<!-- 如果使用的是本地事务(jdbc事务) -->
<property name="hibernate.current_session_context_class">thread</property>
这个的作用是能使用 getCurrentSession()
package app.news.test;
import org.hibernate.Session;
import app.news.dao.hibernate.HibernateSessionFactory;
import app.news.table.User;
import app.news.table.UserInfo;
public class Test {
public static void main(String[] args) {
Session session = HibernateSessionFactory.getSession();
//获取session,这个session是多例,不是单例,每次getSession()获得的都不是同一个对象
//Session csession = HibernateSessionFactory.getSessionFactory().getCurrentSession();
/**
* <session-factory>
<!-- 如果使用的是本地事务(jdbc事务) -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- * 如果使用的是全局事务(jta事务)-->
<!-- <property name="hibernate.current_session_context_class">jta</property>-->
* 将配置放在hihbenrate.cfg.xml session-factory下面
*/
//获取CurrentSession 这个是单例,获得的是同一个对象,只有在多线程的时候才能体现,在这里是测试不出来的
/*
* user 表中有一条记录, user_id=1,username=admin,password=password
* userinfo表中有一条记录,userinfo_id=1,user_id=1,sex=男,name=张三
*/
User user = (User)session.get(User.class, 1);
//参数为实体类的class,id(这里1 被封装成了integer对象)
System.out.println("userid:"+user.getUserId());
System.out.println("username:"+user.getUsername());
System.out.println("password:"+user.getPassword());
UserInfo userinfo = (UserInfo)session.get(UserInfo.class, 1);
System.out.println("userinfo_id:"+userinfo.getUserinfoId());
System.out.println("userinfo_sex:"+userinfo.getSex());
System.out.println("userinfo_name:"+userinfo.getName());
System.out.println("userinfo_user:user_id="+userinfo.getUser().getUserId()+",user_name="+userinfo.getUser().getUsername());
session.close();
}
}
结果为:
Hibernate:
select
user0_.user_id as user1_0_0_,
user0_.password as password0_0_,
user0_.username as username0_0_
from
news.user user0_
where
user0_.user_id=?
userid:1
username:admin
password:password
Hibernate:
select
userinfo0_.userinfo_id as userinfo1_1_0_,
userinfo0_.name as name1_0_,
userinfo0_.sex as sex1_0_,
userinfo0_.user_id as user4_1_0_
from
news.userinfo userinfo0_
where
userinfo0_.userinfo_id=?
userinfo_id:1
userinfo_sex:男
userinfo_name:张三
userinfo_user:user_id=1,user_name=admin
如果之前没有查找user,id=1,则
Hibernate:
select
userinfo0_.userinfo_id as userinfo1_1_0_,
userinfo0_.name as name1_0_,
userinfo0_.sex as sex1_0_,
userinfo0_.user_id as user4_1_0_
from
news.userinfo userinfo0_
where
userinfo0_.userinfo_id=?
userinfo_id:1
userinfo_sex:男
userinfo_name:张三
Hibernate:
select
user0_.user_id as user1_0_0_,
user0_.password as password0_0_,
user0_.username as username0_0_
from
news.user user0_
where
user0_.user_id=?
userinfo_user:user_id=1,user_name=admin
这就是fetch @OneToOne(fetch = FetchType.LAZY) ,为 LAZY的结果
调用get其他表对象引用的时候才去查询.
一般来说用tomcat会提示log4j没有配置 配置了有一个好处,显示它不显示的错误.
既然要用就配置一个
以log4j.properties为名 创建一个文件,
#OFF,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB,ALL
#log4j.rootLogger =ALL,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB
log4j.rootLogger = ALL,systemOut
log4j.appender.systemOut = org.apache.log4j.ConsoleAppender
log4j.appender.systemOut.layout = org.apache.log4j.PatternLayout
log4j.appender.systemOut.layout.ConversionPattern = [%.15p][%-22d{yyyy/MM/dd HH\:mm\:ssS}][%l]%n
log4j.appender.systemOut.Threshold = WARN
log4j.appender.systemOut.ImmediateFlush = TRUE
log4j.appender.systemOut.Target =System.out
log4j.appender.DailyRollingFileAppender.Encoding=UTF-8
第一行的注释,说关闭这些目标输出systemout是控制台输出,也即是MyEclipse的console,其他的有些是输出到文件输出邮件等等.
rootLogger = ALL , all即输出全部级别,有debug ,info,warn,error等
下面的systemOut.Threshold = WARN 即,输出到控制台的最低级别为warn,低于它的消息就不显示了.更多配置网上查.
Action 文件,作为与页面和service的交集,决定了接收请求和请求处理事项.为了理解就从这里开始.
创建一个Action文件位于app.news.controller包下文件名为LoginAction.java 继承 ActionSupport类属于
com.opensymphony.xwork2.ActionSupport
包,而LoginAction.java文件的包名,为controller为之后的一个问题挖了坑.
且看 LoginAction.java
package app.news.controller;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.springframework.stereotype.Controller;
import app.news.table.User;
import com.opensymphony.xwork2.ActionSupport;
@Controller//注入到spring中,有几种:@component 代表中立层 ,@service service层,@Repository dao层
@Results({
@Result(name="error",location="/pages/error.jsp"),
@Result(name="success",location="/index.jsp",type="redirect")
})
public class LoginAction extends ActionSupport {
String username;//用于接收form表单的 用户名
String password;//密码 接收属性的变量必须有set方法
User user;//必须写get and set,普通变量可以不写get,但建议写上
private static final long serialVersionUID = -6309124769328744856L;
@Action(value="/news/login")// /news即 namespace 也可以在类声明前写@NameSpace("news"),然后这里只写value="login"
public String login(){
System.out.println(username+" "+password);
System.out.println(user.getUsername()+" "+user.getPassword());
HttpServletRequest request = ServletActionContext.getRequest();//诚如 asp的request
System.out.println("user.password:"+request.getParameter("user.password"));//获取传递的参数
return SUCCESS;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setUser(User user) {
this.user = user;
}
public User getUser(){
return this.user;
}
}
使用注解@Controller,即将此类的创建交由Spring 框架管理
使用注解则不需要在struts.xml中配置action
主要使用的几个注解:
@Controller 对象创建交给spring托管,
@NameSpace 命名空间 请求的url需要带这里设置的部分
例如 @NameSpace(“news”),这里写了下面的action就不用加/news/
@Actions 一个方法对应多个请求时使用
例如@Actions({@Action(value=”/namespace/actionName”),..})
@Action 一个请求,有请求名name,和返回处理集results属性
例如 @Action(value=”/news/login”[,results={@Result(name=”success”,location=”/index.jsp”,type=”redirect”),…}])
@Results 返回处理集,需要写在类前面
例如 @Results({
@Result(name=”error”,location=”/pages/error.jsp”),
@Result(name=”success”,location=”/index.jsp”,type=”redirect”)
})
@Result 一个返回处理
例如 @Result(name=”success”,location=”/index.jsp”[,type=”redirect”]),其中 type可以为 redirect和redirectAction等等
一般来说,注解的方式,struts默认会扫描包名是action,struts,struts2等的包名中的类(类名以action结尾或继承了action类的类,ActionSupport实现了Action),如果放Action文件的包名不是这些,则需要指定,否者扫描不到.然后文件有了该写配置了,首先是
struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<!-- 请求参数的编码方式 -->
<constant name="struts.i18n.encoding" value="UTF-8"/>
<!-- 当struts.xml改动后,是否重新加载。默认值为false(生产环境下使用),开发阶段最好打开 -->
<constant name="struts.configuration.xml.reload" value="true"/>
<!-- 是否使用struts的开发模式。开发模式会有更多的调试信息。默认值为false(生产环境下使用),开发阶段最好打开 -->
<constant name="struts.devMode" value="false"/>
<constant name="struts.multipart.maxSize" value="1000000000000" />
<!-- 设置浏览器是否缓存静态内容。默认值为true(生产环境下使用),开发阶段最好关闭 -->
<constant name="struts.serve.static.browserCache" value="false" />
<!-- 指定被struts2处理的请求后缀类型。多个用逗号隔开 -->
<constant name="struts.action.extension" value="ahtml"/>
<!-- 指定由spring负责action对象的创建-->
<constant name="struts.objectFactory" value="spring" />
<!-- 自定义jsp文件命名的分隔符 -->
<constant name="struts.convention.action.name.separator" value="-" />
<!-- 该常量指定包作为根包来搜索Action类。
Convention插件除了扫描默认的action,actions,struts,struts2,还会扫描该常量指定的一个或多个包,
Convention会试图从指定包中发现Action类。 -->
<constant name="struts.convention.action.packages" value="app.news.controller" />
</struts>
struts.action.extension 指定请求后缀名,默认是 action,do
这里我改成了 ahtml,请求就得带上 /news/login.ahtml
struts.objectFactory 将Action的创建交由Spring管理,所以 下一个配置
文件
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<!-- 配置数据源:方法一,使用C3P0方式(推荐) -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql://localhost:3306/"
p:user="root"
p:password="123456" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- 配置session factory使用的数据源 -->
<property name="dataSource" ref="dataSource" />
<!--自动扫描实体 -->
<property name="packagesToScan" value="app.news.table" />
<!-- 配置hibernate的其他属性 -->
<property name="hibernateProperties">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<entry key="hibernate.show_sql" value="true" />
<entry key="hibernate.format_sql" value="true" />
<!--
如是说Spring托管则不需要 Hibernate 管理session
开启此线程用于手动管理事务,便于测试,去掉则由spring托管,不能直接使用currentsession
所以用服务器整合测试去掉,用main()测试开启或不使用currentsession
-->
<!--
<entry key="hibernate.current_session_context_class" value="thread" />
-->
</map>
</property>
</bean>
<!-- 加入事务管理切面类的配置-->
<!-- 创建事务管理器(spring针对hibernate实现的事务管理的切面类) -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 用注解来实现事务管理 -->
<tx:annotation-driven transaction-manager="txManager"/>
<!--引用其他配置文件 <import resource="applicationContext-other.xml"/> -->
<!--声明注解所用的bean,但若有下面的scan则不必出现.后者包含前者 <context:annotation-config /> -->
<context:component-scan base-package="app.news.*" />
</beans>
仔细对比原来的xml,我修改了连接池,原来是dbcp,现在是c3p0都可以,还有其他几种也可以.
1. dataSource 配置成了c3p0
2. sessionFactory 配置了其他属性其中
3. packagesToScan 属性设置了扫描地方,即app.news.table包
4. txManager 也就是之前的transcationManager,事务管理器
5. <tx>标签需要声明,也就是xml开头的那些链接.xsd
6. tx:annotation-driven 注解方式实现事务,管理器使用了txManager
7. transactionAdvice 事务通知
8. bean hibernateTemplate 方便使用session的工具
9. context:component-scan 扫描使用
10. @Service@Controller@Repository的类,并管理创建他们
11. hibernate.current_session_context_class 作为单独使用hibernate时使用,当使用Spring事务时需要去掉
值得一提的是
<bean id=”hibernateTemplate”
class=”org.springframework.orm.hibernate3.HibernateTemplate”>
Hibernate4 支持了事务并且与Spring3.1的HibernateTemplate冲突 所以需要使用Hibernate提供的session获得方式.例如MyEclipse提供创建的HibernateSessionFactory.java
核心配置文件就是web.xml,struts.xml,applicationContext.xml
hibernate.cfg.xml 无用处.
ps:一般 There is no Action mapped,确认你的包名是否是action/struts
如果不是需要设置struts中的struts.convention.action.packages
若使用了@Controller再检查applicationContext.xml中的
<context:component-scan base-package="app.news.*" />
若还是没有,看你的请求后缀名是否一致,namespace+action名=url+后缀,再就是看,action文件是否以action结尾,或者 此文件是否继承 ActionSupport类.
login.jsp
<form action="/news/login.ahtml" method="post">
username:<input type="text" name="username" /> <br />
password:<input type="password" name="password" /> <br />
user.username:<input type="text" name="user.username" /> <br />
user.password:<input type="password" name="user.password" /> <br />
<input type="submit" value="login" />
<input type="reset" value="reset" />
</form>
为了测试区别两种方式一起,之后查询时使用后者 user对象去查找
输入
admin
admin
useradmin
userpassword
结果为
admin admin//String username , password
useradmin userpassword//User user.username , user.password
user.password:userpassword//request.getParameter("user.password") 此处的user.password是表单中 input标签的name
能得到这些,就可以进行下一步了
前面的都是测试,之后的才是正文
到此注解方面是没有问题了,但还没有写dao,service ,所以开始写吧.
DAO
接口
BaseDAO.java
/**
* @Title: BaseDAO.java
* @Package com.information.dao
* @Description: TODO(基础数据操作接口)
* @author Mr.Wang
* @date 2016年6月13日 上午10:21:37
* @version V1.0
*/
package com.information.dao;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import org.hibernate.criterion.Criterion;
/**
* @ClassName: BaseDAO
* @Description: TODO(底层基础操作接口)
* @author Mr.Wang
* @param <T> 任意实体类
* @date 2016年6月13日 上午10:26:54
*/
public interface BaseDAO<T>{
/**
* @Title: 添加
* @Description: TODO(保存所设类型的对象,序列化用于生成主键)
* @param @param o
* @param @return 设定文件
* @return Serializable 返回生成的主键
*/
public Serializable save(T o);
/**
* @Title: 删除
* @Description: TODO(删除一个记录)
* @param @param o 要删除的对象,包含主键
*/
public void delete(T o);
/**
* 更新一个对象
*
* @param o
*/
public void update(T o);
/**
* 添加或修改对象
*
* @param o
*/
public void saveOrUpdate(T o);
/**
* 查询
* @Description: TODO(以完整的 hql语句查询)
* @param hql from 类名 where field=n
* @return
*/
public List<T> find(String hql);
/**
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param c 类.class
* @param @param crit 规则 Property.eq() Restrictions.eq()...
* @return T 返回类型
*/
public List<T> find(Class<T> c,Criterion criterion);
/**
* 查询集合
*
* @param hql ,from 类名 where field = ? and field = ?
* @param param ,数组代表按顺序代表hql中的?
* @return 泛型列表
*/
public List<T> find(String hql, Object[] param);
/**
* 查询集合
*
* @param hql ,from 类名 where field = ? and field = ?
* @param param ,list集合 按顺序代表hql中的?
* @return 泛型列表
*/
public List<T> find(String hql, List<Object> param);
/**
* 查询集合
*
* @param hql ,from 类名 where field = :param1 and field = :param2
* @param param ,map键值对集合 按名称代表hql中的?
* @return 泛型列表
*/
public List<T> find(String hql, Map<String,Object> param);
/**
* 查询集合(带分页)
*
* @param hql ,from 类名 where field = ? and field = ?
* @param param ,数组参数顺序对应?位置
* @param page
* 查询第几页
* @param rows
* 每页显示几条记录
* @return
*/
public List<T> find(String hql, Object[] param, Integer page, Integer rows);
/**
* 查询集合(带分页)
*
* @param hql ,from 类名 where field = ? and field = ?
* @param param ,集合参数顺序对应?位置
* @param page
* 查询第几页
* @param rows
* 每页显示几条记录
* @return
*/
public List<T> find(String hql, List<Object> param, Integer page,
Integer rows);
/**
* 查询集合(带分页)
*
* @param hql ,from 类名 where field = ? and field = ?
* @param param ,Map集合参数顺序对应?位置
* @param page
* 查询第几页
* @param rows
* 每页显示几条记录
* @return
*/
public List<T> find(String hql, Map<String,Object> param, Integer page,
Integer rows);
/**
* 获得一个对象
*
* @param c 查询类的class
* 对象类型
* @param id 主键,联合主键则是一个完整对象
* @return Object
*/
public T get(Class<T> c, Serializable id);
/**
* 获得一个对象
* 同find() 唯一不同只是返回一个元素
* @param hql
* @param param
* @return Object
*/
public T get(String hql, Object[] param);
/**
* 获得一个对象
*
* @param hql
* @param param
* @return
*/
public T get(String hql, List<Object> param);
/**
* 获得一个对象
*
* @param hql
* @param param
* @return
*/
public T get(String hql, Map<String,Object> param);
/**
* select count(*) from 类
*
* @param hql
* @return
*/
public Long count(String hql);
/**
* select count(*) from 类
*
* @param hql
* @param param
* @return
*/
public Long count(String hql, Object[] param);
/**
* select count(*) from 类
*
* @param hql
* @param param
* @return
*/
public Long count(String hql, List<Object> param);
/**
* 执行HQL语句
*
* @param hql ,from 类名 where field = n
*/
public Integer executeHql(String hql);
/**
* 执行HQL语句
* 修改操作
* @param hql ,from 类名 where field = ? and field = ?
* @param param
* @return 响应数目
*/
public Integer executeHql(String hql, Object[] param);
/**
* 执行HQL语句
* 修改操作
* @param hql ,from 类名 where field = ? and field = ?
* @param param
* @return 返回修改的记录数
*/
public Integer executeHql(String hql, List<Object> param);
/**
* 执行HQL语句
* 修改操作
* @param hql ,from 类名 where field = :param1 and field = :param2
* @param param
* @return 返回修改的记录数
*/
public Integer executeHql(String hql, Map<String,Object> param);
}
实现类
BaseDAOImpl.java
package com.information.dao.impl;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.springframework.stereotype.Repository;
import com.information.dao.BaseDAO;
@Repository("baseDAO")
@SuppressWarnings("all")
public class BaseDAOImpl<T> implements BaseDAO<T> {
@Resource
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
private Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
@Override
public Serializable save(T o) {
return this.getCurrentSession().save(o);
}
@Override
public void delete(T o) {
this.getCurrentSession().delete(o);
}
@Override
public void update(T o) {
this.getCurrentSession().update(o);
}
@Override
public void saveOrUpdate(T o) {
this.getCurrentSession().saveOrUpdate(o);
}
@Override
public List<T> find(String hql) {
return this.getCurrentSession().createQuery(hql).list();
}
@Override
public List<T> find(Class<T> c,Criterion criterion){
Criteria crit = this.getCurrentSession().createCriteria(c);
crit.add(criterion);
return crit.list();
}
@Override
public List<T> find(String hql, Object[] param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.length > 0) {
for (int i = 0; i < param.length; i++) {
q.setParameter(i, param[i]);
}
}
return q.list();
}
@Override
public List<T> find(String hql, List<Object> param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (int i = 0; i < param.size(); i++) {
q.setParameter(i, param.get(i));
}
}
return q.list();
}
@Override
public List<T> find(String hql, Map<String, Object> param) {
Query q = this.getCurrentSession().createQuery(hql);
if(param != null && param.size() > 0 )
for (String key : param.keySet()) {
q.setParameter(key, param.get(key));
}
return q.list();
}
@Override
public List<T> find(String hql, Object[] param, Integer page, Integer rows) {
if (page == null || page < 1) {
page = 1;
}
if (rows == null || rows < 1) {
rows = 10;
}
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.length > 0) {
for (int i = 0; i < param.length; i++) {
q.setParameter(i, param[i]);
}
}
return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
}
@Override
public List<T> find(String hql, List<Object> param, Integer page,
Integer rows) {
if (page == null || page < 1) {
page = 1;
}
if (rows == null || rows < 1) {
rows = 10;
}
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (int i = 0; i < param.size(); i++) {
q.setParameter(i, param.get(i));
}
}
return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
}
@Override
public List<T> find(String hql, Map<String, Object> param, Integer page,
Integer rows) {
if (page == null || page < 1) {
page = 1;
}
if (rows == null || rows < 1) {
rows = 10;
}
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (String key : param.keySet()) {
q.setParameter(key, param.get(key));
}
}
return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
}
@Override
public T get(Class<T> c, Serializable id) {
return (T) this.getCurrentSession().get(c, id);
}
@Override
public T get(String hql, Object[] param) {
List<T> l = this.find(hql, param);
if (l != null && l.size() > 0) {
return l.get(0);
} else {
return null;
}
}
@Override
public T get(String hql, List<Object> param) {
List<T> l = this.find(hql, param);
if (l != null && l.size() > 0) {
return l.get(0);
} else {
return null;
}
}
@Override
public T get(String hql,Map<String,Object> param){
List <T> l = this.find(hql,param);
if (l != null && l.size() > 0) {
return l.get(0);
} else {
return null;
}
}
@Override
public Long count(String hql) {
return (Long) this.getCurrentSession().createQuery(hql).uniqueResult();
}
@Override
public Long count(String hql, Object[] param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.length > 0) {
for (int i = 0; i < param.length; i++) {
q.setParameter(i, param[i]);
}
}
return (Long) q.uniqueResult();
}
@Override
public Long count(String hql, List<Object> param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (int i = 0; i < param.size(); i++) {
q.setParameter(i, param.get(i));
}
}
return (Long) q.uniqueResult();
}
@Override
public Integer executeHql(String hql) {
return this.getCurrentSession().createQuery(hql).executeUpdate();
}
@Override
public Integer executeHql(String hql, Object[] param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.length > 0) {
for (int i = 0; i < param.length; i++) {
q.setParameter(i, param[i]);
}
}
return q.executeUpdate();
}
@Override
public Integer executeHql(String hql, List<Object> param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (int i = 0; i < param.size(); i++) {
q.setParameter(i, param.get(i));
}
}
return q.executeUpdate();
}
@Override
public Integer executeHql(String hql, Map<String, Object> param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (String key :param.keySet()) {
q.setParameter(key, param.get(key));
}
}
return q.executeUpdate();
}
}
Service
例如用户接口
/**
* @Title: LoginService.java
* @Package com.information.service.impl
* @Description: TODO(登录操作)
* @author Mr.Wang
* @date 2016年6月13日 下午3:50:04
* @version V1.0
*/
package com.information.service;
import com.information.table.User;
/**
* @ClassName: LoginService
* @Description: TODO(用于用户相关服务)
* @author Mr.Wang
* @date 2016年6月13日 下午3:50:04
*/
public interface UserService {
/**
* @Title: checkLogin 验证登录
* @Description: TODO(用以验证登录信息)
* @param @param user 用户对象 Administrator
* @return User 返回匹配对象
*/
public User checkLogin(User user);
/**
* @Title: modifyUser 修改用户
* @Description: TODO(修改用户密码)
* @param @param user 用户对象
* @return boolean 修改成功与否
*/
public boolean modifyUserPassword(User user);
}
实现类
/**
* @Title: UserServiceImpl.java
* @Package com.information.service.impl
* @Description: TODO(用于User表相关操作)
* @author Mr.Wang
* @date 2016年6月13日 下午3:59:43
* @version V1.0
*/
package com.information.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.hibernate.criterion.Property;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.information.dao.BaseDAO;
import com.information.service.UserService;
import com.information.table.User;
/**
* @ClassName: UserServiceImpl
* @Description: TODO(用于User表相关操作)
* @author Mr.Wang
* @date 2016年6月13日 下午3:59:43
*/
@Transactional
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource(name="baseDAO")
BaseDAO<User> dao;
@Override
public User checkLogin(User user) {
if(user == null)return null;
Property uname = Property.forName("username");
List<User> list = dao.find(User.class, uname.eq(user.getUsername()));
if(list!=null && list.size()>0){
User temp = list.get(0);
if(temp.equals(user.getPassword()))return temp;
}
return null;
}
@Override
public boolean modifyUserPassword(User user) {
if(user == null)return false;
if(user.getPassword() == null || user.getPassword().length()>25 || user.getPassword().length() < 5)return false;
String hql= "update User user set user.password=? where user.userId="+user.getUserId();
int changeCount = dao.executeHql(hql,new String[]{user.getPassword()});
if(changeCount>0)return true;
return false;
}
}
一开始我还打算每个表写一个dao,看了别人的代码才有所感悟,底层写一个就是了,UserService只提供两个(暂时)接口,一个check,一个modify,但实现这两个方法的内部就不一定了
内部:
public User checkLogin(User user) {
if(user == null)return null;//空就返回
Property uname = Property.forName("username");
List<User> list = dao.find(User.class, uname.eq(user.getUsername()));
//这里其实使用的是
//public List<T> find(Class<T> c,Criterion criterion);
//内部
/*
Criteria crit = this.getCurrentSession().createCriteria(c);
crit.add(criterion);//标准查询,按照标准规则设置查询条件查询
return crit.list();
*/
//它需要一个Criterion对象,这个对象可由
//property.forname("类属性名").eq("类属性值")获得
//当然Property类提供了大量的方法例如,eq/isNull/ne/or/and/order等,相当于== <> or and
if(list!=null && list.size()>0){
User temp = list.get(0);
if(temp.equals(user.getPassword()))return temp;
}
return null;
}
此处没有使用HQL所以没发现一个问题,而modify则发现了
public boolean modifyUserPassword(User user) {
if(user == null)return false;//空则返回false
if(user.getPassword() == null || user.getPassword().length()>25 || user.getPassword().length() < 5)return false;//密码不符合规则返回false,相当于再次确认当然应该用正则表达式来更好
String hql= "update User user set user.password=? where user.userId="+user.getUserId();//HQL语句
int changeCount = dao.executeHql(hql,new String[]{user.getPassword()});//使用数组参数
if(changeCount>0)return true;//判断修改数确定返回值
return false;
}
此处使用 dao.executeHql(),参数为 String hql, Object[] param
使用setParameter(index,param)的形式 ,而这就是一个配置问题的入口
使用HQL,需要创建
Query q = this.getCurrentSession().createQuery(hql);
而创建Query,中含有参数的时候却有一个异常抛出
"java.lang.NoSuchMethodError: antlr.collections.AST.getLine()I"
引起异常的是 jar包冲突,一个是 Struts中的 antlr2.7.2另一个是Hibernate的antlr2.7.7,去掉2.7.2即可
在window-preferences中
至此,查询和修改都没错误了,不知道之后还有什么情况
2017年5月22日 补充:具体引用的包
Spring
Struts
Hibernate
若有错-To be continue…