基于maven的spring项目实现登录注册(SSM)
先前参考这位大佬的文章写项目出了很多问题,经过不断修改之后终于项目得以平稳运行了。
以下是我的踩坑记录以及对应的de解决方案(不过有一个问题是解决了但是我不懂深层次理由,对此有理解的友友希望可以帮我解答。)
首先上出现最多最严重以及我没理解报错原理的一个问题。
大概就是我已经定义了sqlsessionFactory的bean,但项目无法识别出来无法创建sqlsessionFactory对象。
解决的方案仅仅是删掉以下代码,大家可以对比一下,上面是正确的,下面是最初我错误的那一版。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引用上面已经配置好的数据库连接池 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置mapper接口的扫描器,将Mapper接口的实现类自动注入到IoC容器中 实现类Bean的名称默认为接口类名的首字母小写 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- basePackage属性指定自动扫描mapper接口所在的包 -->
<property name="basePackage" value="com.test.dao"/>
</bean>
<!--上面正确,下面的是错的-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<property name="configLocation" value="classpath:mybatis/config.xml" />
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" >
<list>
<value>classpath*:mybatis/mapper-user.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引用上面已经配置好的数据库连接池 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置mapper接口的扫描器,将Mapper接口的实现类自动注入到IoC容器中 实现类Bean的名称默认为接口类名的首字母小写 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- basePackage属性指定自动扫描mapper接口所在的包 -->
<property name="basePackage" value="com.test.dao"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<property name="configLocation" value="classpath:mybatis/config.xml" />
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath*:mybatis/mapper-user.xml" >
</property>
</bean>
不一致的部分是两版不同,初版是通过mapper-user.xml文件加载SQL语句,而第二版是存在一个userMapper的接口类来加载SQL语句。让人在意的是标了红色不一致的这行代码。
查了一下说法是:
<property name="configLocation" value="classpath:mybatis-config.xml"/>
单独新建一个mybatis-config.xml
文件,然后依赖注入进Spring
容器里
spring容器里引入mybatis-config,mybatis-config里有Mapper映射文件地址就直接获取了。
但我运行的时候出现了严重的问题
删除这一行并且添加:
<!-- basePackage属性指定自动扫描mapper接口所在的包 -->
<property name="basePackage" value="com.test.dao"/>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
通过MapperScannerConfigurer来扫描Dao包里面的mapper接口,动态的将mapper接口进行实例化;
2、并将sqlSessionFactory注入到mapper实例中并自动创建sqlsession。
运行成功!
按理说我原本的配置了config.xml文件,它应该能能够索引到,但是不知道为什么无法创建sqlsession对象还报错没有相关的bean。
接下来是整个项目的流程。
创建项目没啥好说的。
主要是项目结构不要出错,接下来我们先要明确需求——完成登录和注册两个功能,那么就需要登录方法和查询用户的SQL语句和注册方法。
准备工作如下:
pom文件所需要的依赖:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.7.RELEASE</version> </dependency> <!-- Spring-JDBC 包含对Spring对JDBC数据访问进行封装的所有类。外部依赖spring-beans,spring-dao --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.7.RELEASE</version> </dependency> <!--MyBatis包 --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.2</version> </dependency> <!-- MyBatis整合Spring的适配包 --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <!-- 数据库连接池、驱动 --> <!-- https://mvnrepository.com/artifact/c3p0/c3p0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!-- JSP:标准标签库 引入jstl/jar保证jsp页面可以使用EL表达式 --> <!-- https://mvnrepository.com/artifact/jstl/jstl --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <!-- provided表明该包只在编译和测试的时候用, 当启动tomcat(在其中有servlet-api包)的时候,就不会冲突了--> <scope>provided</scope> </dependency> <!-- 单元测试junit包 --> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--与json有关的包--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.6</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.6</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.6</version> </dependency> </dependencies>
准备好tomcat,这里选用的是tomcat8版本,pom文件依赖的版本根据自己实际情况更改。
准备好这些之后,我们要在源文件夹写入相关的类和实现业务。
如果Java文件夹不是蓝色的,前往项目结构-模块,选中自己的项目,在src目录下找到Java目录,选中Java目录然后点击原码,Java就变蓝了,同理resource文件如果没有资源目录标识也是选中resource目录然后点击有跟源码同一栏的Recourse即可。
接下来是准备好实体类在pojo(domain)或者你自己实体类的包下面。
创建实体类步骤很简单,就是访问数据库所需要的字段、关于字段的有参无参构造和getset方法以及根据需要要不要生成toString();
大致这个样子。
按照流程走,接下来是需要准备好数据库驱动的连接。(数据库加载驱动和连接的参数XXX.properties)
一般内容如下;
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/你要连接的数据库名称 jdbc.username=你数据库的用户名 jdbc.password=你访问数据库的密码
为了能方便加载驱动,我们在spring-context.xml文件里面配置bean来获取properties文件的内容
我用的是c3p0,德鲁伊的要用德鲁伊的class。
<!-- 3.加载数据库的属性文件并配置数据库连接池(c3p0) -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
配置成功你的properties文件应该会变橙色:
配置好数据库之后开始程序员的老本行——CRUD(增删改查)
login功能对应查找select
注册功能对应增加insert
我们可以在dao层自己创建一个mapper接口类来放置这下SQL语句。如下图所示:
@Mapper
@Component
public interface UserMapper {
//增加用户
@Insert("insert into user (username,password,name) values (#{username},#{password},#{name})")
void insert(@Param("username") String username, @Param("password") String password, @Param("name") String name);
//查询用户
@Select("select * from user where username = #{username} and password=#{password}")
User findAll(@Param("username") String username, @Param("password") String password);
}
也可以编写一个mapper.xml文件,甚至这个文件更好,可以动态抽取SQL语句,大致内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper">
<!--sql语句抽取-->
<sql id="selectUser">select * from user</sql>
<select id="findByCondition" parameterType="user" resultType="user">
<include refid="selectUser"></include>
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
<select id="findByIds" parameterType="list" resultType="user">
<include refid="selectUser"></include>
<where>
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
</mapper>
之后就是老流程dao-service-serviceImpl。
@Mapper
@Component
public interface UserMapper {
//增加用户
@Insert("insert into user (username,password,name) values (#{username},#{password},#{name})")
void insert(@Param("username") String username, @Param("password") String password, @Param("name") String name);
//查询用户
@Select("select * from user where username = #{username} and password=#{password}")
User findAll(@Param("username") String username, @Param("password") String password);
}
由于前后端分离,我们需要把查询到的数据封装为前端数据对象,因此注意!
需要写一个封装类名字自定义来封装我们的数据!千万不能忘记!
package com.test.bean; import org.springframework.stereotype.Component; import java.io.Serializable; /** * 用于封装后端返回前端数据对象 */ @Component public class ResultInfo implements Serializable { private boolean flag;//后端返回结果正常为true,发生异常返回false private Object data;//后端返回结果数据对象 private String errorMsg;//发生异常的错误消息 //无参构造方法 public ResultInfo() { } public ResultInfo(boolean flag) { this.flag = flag; } /** * 有参构造方法 * @param flag * @param errorMsg */ public ResultInfo(boolean flag, String errorMsg) { this.flag = flag; this.errorMsg = errorMsg; } /** * 有参构造方法 * @param flag * @param data * @param errorMsg */ public ResultInfo(boolean flag, Object data, String errorMsg) { this.flag = flag; this.data = data; this.errorMsg = errorMsg; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } }
之后就是编写控制类controller来实现我们的需求。
需要注意的是在我自己后端测试的时候是get方式,需要提交前端是则是post,要理解两种方式的区别。
package com.test.controller;
import com.test.bean.ResultInfo;
import com.test.bean.User;
import com.test.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
@Controller
@RequestMapping(value = "user")
public class DispatcherController {
@Resource
private UserService userService;
@Resource
private ResultInfo resultInfo;
@RequestMapping(value = "/login" ,method = RequestMethod.POST)
@ResponseBody
public ResultInfo login(User user) {
User userAll = userService.findAll(user.getUsername(),user.getPassword(),user.getName());
if (userAll != null ) {
resultInfo.setFlag(true);
resultInfo.setErrorMsg("登录成功");
} else {
resultInfo.setFlag(false);
resultInfo.setErrorMsg("账户名或密码错误");
}
return resultInfo;
}
@RequestMapping(value = "/register" ,method = RequestMethod.POST)
@ResponseBody
public ResultInfo register(String username, String password, String email,String name) {
User name1 = userService.findName(username);
if (name1 != null) {
resultInfo.setFlag(false);
resultInfo.setErrorMsg("用户名已存在");
} else {
userService.insert(username, password, email,name);
resultInfo.setFlag(true);
resultInfo.setErrorMsg("注册成功");
}
return resultInfo;
}
}
监听器和拦截器留在下章说,之前因为拦截器逻辑设计有点问题——比如我设置了一个未登录用户会自动跳转登录用户,正常来说应该是跳转首页,直接变成了一刀切,就无法完成我的注册需求了。