Spring学习3(1)
maven的配置其实是不繁琐的,在学习了基本的环境配置后就可以正式对spring的业务层,持久层,展现层进行一个基本的了解。我是根据《精通Spring 4.x 企业应用开发实战一步步做的》,不过这本书所用的工具可能有一点和我所使用的不同(我的编辑器是eclipse。
基本配置
由于是一个用户登陆页面,所以我们需要如下页面:
- login.jsp:用来进入登陆页面并且提交用户名密码表单到服务器。
- LoginController:用来判断用户是否存在以及密码是否匹配,如果不存在就重定向回login.jsp,如果存在就更新用户登陆时间以及增加用户积分,并定向到欢迎界面。
- main.jsp:当用户存在时,可以访问的欢迎界面。
根据上述需求需要对数据进行设计,创建名为“sampledb”的数据库,并创建所用的两张表格,具体代码如下:
use sampledb;
create table t_user(
user_id int auto_increment primary key,
user_name varchar(30),
credits int,
password varchar(32),
last_visit datetime,
last_ip varchar(23)
)engine=InnoDB;
create table t_login_log(
login_log_id int auto_increment primary key,
user_id int,online_goodsonline_goods
ip varchar(23),
login_datetime datetime
)engine=innoDB;
insert into t_user(user_name, password)
values('admin', '123456');
commit;
这里不再给出工程创建的过程,不过给出pom.xml的内容:
...
<properties>
<spring.version>4.1.6.RELEASE</spring.version>
<servlet.version>2.5</servlet.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
...
需要注意的是:
- 这里的< version >可以去Maven Repository去自行查找,这里的版本如果本机没有maven就会自动下载。
- 这里在测试(maven install)时可能会出现:“No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?”错误,可以参考这个stackoverflow的回答解决,其实就是在window->reference里配置jre时要用jdk的地址而不是jre的地址。
持久层
持久层是负责数据的访问和操作的,data access object(DAO)类被上层的业务层使用,这里使用spring jdbc作为Object-relational mapping(ORM)。
建立领域对象
Domain Object也被称为实体类,代表了业务的状态,贯穿了展现层,业务层和持久层。它使数据库表操作以面向对象的方式进行,它不一定等同于数据库表。
这里我们需要两个领域对象,刚好对应两张表,并且每个字段对应一个对象属性。
那么首先在main/java/com/smart(这是我的工程路径/com/smart可以自己取)下创建一个新的package:domian,而后在其下创建User.java代码如下:
package com.smart.domain;
import java.io.Serializable;
import java.util.Date;
//领域对象为了序列化,一般要实现Serializable接口
public class User implements Serializable{
private int userId, credits;
private String userName, password, lastIp;
private Date lastVisit;
public void setCredits(int credits) {
this.credits = credits;
}
public Date getLastVisit() {
return this.lastVisit;
}
//剩下的方法以此类推
}
和LoginLog.java:
package com.smart.domain;
import java.io.Serializable;
import java.util.Date;
public class LoginLog implements Serializable{
private int LoginLogId, userId;
private String ip;
private Date loginDate;
}
建立Dao
我们用Dao来控制对数据的操作,这里User的Dao需要的方法如下:
- getMatchCount():根据用户名和密码获取匹配的用户数
- findUserByUserName():根据用户名获取User object
- updateLoginLog():更新用户信息(积分,登陆ip等)
那么我们首先先在domain同一级目录下创建dao目录,而后在其目录下创建UserDao.java,代码如下:
package com.smart.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import com.smart.domain.User;
@Repository
public class UserDao{
private JdbcTemplate jdbcTemplate;
private final static String MATCH_COUNT_SQL = " SELECT count(*) FROM " +
" t_user WHERE user_name =? and password =? ";
private final static String UPDATE_LOGIN_INFO_SQL = " UPDATE t_user SET " +
" last_visit=?,last_ip=?,credits=? WHERE user_id =? ";
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int getMatchCount(String userName, String password) {
return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[] {userName, password}, Integer.class);
}
public User findUserByUserName(final String userName) {
final User user = new User();
String sqlStr = " SELECT user_id, user_name, credits from " +
" t_user WHERE usr_name=? ";
jdbcTemplate.query(sqlStr, new Object[] {userName},
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException{
user.setUserId(rs.getInt("user_id"));
user.setUserName(userName);
user.setCredits(rs.getInt("credits"));
}
});
return user;
}
public void updateLoginInfo(User user) {
jdbcTemplate.update(UPDATE_LOGIN_INFO_SQL,new Object[] {user.getLastVisit(),
user.getLastIp(), user.getCredits(), user.getUserId()
});
}
}
首先这里出现了几个疑问点:
- 之前在pom.xml里没有配置好的dependency,所以我们里需要再到配置文件中添加spring-jdbc, spring-context的依赖。
- 这里
getMatchCount
方法中名为queryForObject
的function也要注意,因为这是3.1版本后的function,原来是queryForInt(sql, object[])
。 - 这里
@Repository
使用注解的方式来定义一个Bean,并使用@Autowired
将spring容器中的Bean注解进来。关于具体的spring注解后面会具体介绍,这里可以知道相较于传统的Bean,Spring Bean更关注于功能本身而将链接数据库等重复的过程进行了封装。 - 这里
jdbcTemplate.query
方法签名为query(String sql, Object[] args, RowCallbackHandler)
,其中RowCallbackHandler
这个入参是用来负责查询结果的处理回调接口,负责将查询结构从ResultSet装载到类似领域对象的对象实例中。 - 有一个小技巧就是在编写sql语句时,如果要分行写,尽量首位都留有空格一方sql语句出错。
LoginLogDao的代码相对简单,仅仅是更新loginlog,代码如下:
package com.smart.dao;
import com.smart.domain.LoginLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class LoginLogDao{
private JdbcTemplate jdbcTemplate;
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private final static String INSERT_LOGIN_LOG_SQL = " INSERT INTO t_login_log( " +
"user_id, ip, login_datetime) Values(?,?,?) ";
public void insertLoginLog(LoginLog loginLog) {
jdbcTemplate.update(INSERT_LOGIN_LOG_SQL, new Object[]
{loginLog.getUserId(), loginLog.getIp(),
loginLog.getLoginDate()});
}
}
在spring中装配Dao
从上述代码中我们可以看到我们并没有写关于链接数据库的代码,这是因为这些功能被jdbcTemplate封装起来了,那么jdbcTemplate本身是需要一个Datasource来获取返回连接从而使用注入。
首先在src/main目录下创建resources目录,而后在此目录下创建smart_context.xml文件,其中代码如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 1 扫描类包,将标注spring注解的类自动转化Bean, 同时完成Bean的注入 -->
<context:component-scan base-package="com.smart.dao"/>
<!-- 2 配置数据源,即链接数据库 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/sampledb"
p:username="root"
p:password="rootroot"
/>
<!-- 3 配置jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource" />
</beans>
- 这里1处使用
<context:component-scan>
来扫描指定类包下的所有类,这样在类中定义的spring注解(@Repository, @Autowired)才能使用。 - 2处就是利用驱动器来链接数据库
- 3处是配置了jdbcTemplate Bean,将2处声明的dataSource注入jdbcTemplate,进而注入Dao中。