Java SpringMVC-SSM 整合
学习视频:B站 狂神说Java – https://www.bilibili.com/video/BV1aE41167Tu?p=1
学习文档: 微信公众号 狂神说 --https://mp.weixin.qq.com/mp/homepage?__biz=Mzg2NTAzMTExNg==&hid=3&sn=456dc4d66f0726730757e319ffdaa23e&scene=18&uin=&key=&devicetype=Windows+10+x64&version=63020170&lang=zh_CN&ascene=7&fontgear=2
1、环境要求
环境:
- IDEA
- MySQL 5.7.19
- Tomcat 9
- Maven 3.6
要求:
- 需要熟练掌握MySQL数据库,Spring,JavaWeb及MyBatis知识,简单的前端知识;
对应环境的级别
创建一个项目的基本步骤包括:
需求分析 ----> 设计数据库 ----> 业务 ----> 前端界面
2、存放书籍数据项目的SSM整合
Mybatis层、Spring层
2.1、数据库环境
需要创建一个存放书籍数据的数据库表,这样才能去调用模型 Model 中的数据,有地方 有数据可调用。
创建数据库表的代码:
CREATE DATABASE `ssmbuild`;
USE `ssmbuild`;
DROP TABLE IF EXISTS `books`;
CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id',
`bookName` VARCHAR(100) NOT NULL COMMENT '书名',
`bookCounts` INT(11) NOT NULL COMMENT '数量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES
(1,'Java',1,'从入门到放弃'),
(2,'MySQL',10,'从删库到跑路'),
(3,'Linux',5,'从进门到进牢');
结果:
2.2、基本环境搭建
- 我们新建一个Maven项目, 为 ssmbuild。并且添加web的支持;
- 导入相关的pom依赖。 需要导入的依赖 jar包有: junit、数据库驱动、数据库连接池;Servlet ,JSP,mybatis,mybatis-spring, spring。 以及需要导入静态资源导出问题。
maven依赖:
<!--maven依赖:unit、数据库驱动、数据库连接池;Servlet ,JSP,mybatis,mybatis-spring, spring-->
<dependencies>
<!--Junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--Servlet - JSP -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
maven静态资源导出问题:
<!--静态资源导出问题-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
3.连接数据库。 连接数据库中对应的 ssmbuild 表。
4.建立项目所需要的基本结构和配置框架:
- com.AL.pojo。实体类,属性对应着数据库表的字段
- com.AL.dao。dao层,属于Model 的数据,进行底层的数据调度
- com.AL.service。service层,属于Model 的业务,进行底层的业务处理
- com.AL.controller。 controller层,控制层,servlet,去创建数据模型,访问数据库,并与视图结合后返回给请求者。
- mybatis-config.xml。这个是 mybatis的配置文件
- applicationContext.xml 。 总的各个框架的,集合后的核心配置文件。
2.2.1、Mybatis层编写
1、数据库配置文件 database.properties。
这个是用来连接数据库的配置文件,表明你连接的哪个数据库。 如果使用的是 Mysql8.0+,能够增加时区配置。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
2、IDEA关联数据库.
3、编写MyBatis的核心配置文件.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.AL.pojo"/>
</typeAliases>
<mappers>
<mapper resource="com/AL/dao/BookMapper.xml"/>
</mappers>
</configuration>
4、编写数据库对应的实体类 com.AL.pojo.Books。
使用lombok插件,在xml配置文件中导入 maven依赖:
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
<scope>provided</scope>
</dependency>
实体类:class Books。 属性对应着数据库字段名。
并采用Lombok进行注解开发,@data,以及有参无参构造器:@AllArgsConstructor,@NoArgsConstructor
package com.AL.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int bookID;
private String bookName;
private int bookCounts;
private String detail;
}
5、编写Dao层的 Mapper接口!
package com.AL.dao;
import com.AL.pojo.Books;
import java.util.List;
public interface BookMapper {
//增加一个Book
int addBook(Books book);
//根据id删除一个Book
int deleteBookById(int id);
//更新Book
int updateBook(Books books);
//根据id查询,返回一个Book
Books queryBookById(int id);
//查询全部Book,返回list集合
List<Books> queryAllBook();
}
6、编写接口对应的 Mapper.xml 文件。需要导入MyBatis的包; 接口实现类
导入MyBatis的包:在这里,我们就能利用Mybatis去做:在BookMaper.xml文件中,可以直接将mybatis-config.xml配置文件更改就能使用:即 configuration改为mapper即可。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.dao.BookMapper">
</mapper>
-
接口实现类: BookMapper.xml (为dao层接口实现类)
在这里注册了 mapper 映射了 实现类的地址,即BookMapper接口被实现。
在这个mapper文件中定义了sql语句 对应着接口中的方法:对数据库操作的方法 sql
在什么时候采用 parameterType 什么时候采用 resultType?【对于返回结果就是 resultType,向其中添加修改元素就是 parameterType】
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.dao.BookMapper">
<!--增加一个Book-->
<insert id="addBook" parameterType="Books">
insert into ssmbuild.books(bookName,bookCounts,detail)
values (#{bookName}, #{bookCounts}, #{detail})
</insert>
<!--根据id删除一个Book-->
<delete id="deleteBookById" parameterType="int">
delete from ssmbuild.books where bookID=#{bookID}
</delete>
<!--更新Book-->
<update id="updateBook" parameterType="Books">
update ssmbuild.books
set bookName = #{bookName},bookCounts = #{bookCounts},detail = #{detail}
where bookID = #{bookID}
</update>
<!--根据id查询,返回一个Book-->
<select id="queryBookById" resultType="Books">
select * from ssmbuild.books
where bookID = #{bookID}
</select>
<!--查询全部Book-->
<select id="queryAllBook" resultType="Books">
SELECT * from ssmbuild.books
</select>
</mapper>
-
在mybatis-config.xml配置文件中注册绑定 我们的dao层接口:这里注册映射文件,采用的是 类方法, 不是绑定接口实现类;
在前面的 mybatis-config.xml文件中已经进行了注册绑定。【采用 resource = ,绑定类方法】
<mappers> <mapper resource="com/AL/dao/BookMapper.xml"/> </mappers>
7.编写Service层的接口和实现类
接口:BookService:底下需要去实现,调用dao层
package com.AL.service;
import com.AL.pojo.Books;
import java.util.List;
//BookService:底下需要去实现,调用dao层
public interface BookService {
//增加一个Book
int addBook(Books book);
//根据id删除一个Book
int deleteBookById(int id);
//更新Book
int updateBook(Books books);
//根据id查询,返回一个Book
Books queryBookById(int id);
//查询全部Book,返回list集合
List<Books> queryAllBook();
}
实现类:调用dao层的操作,设置一个set接口,方便Spring管理【set注入,IOC容器注册实现Bean】
package com.AL.service;
import com.AL.dao.BookMapper;
import com.AL.pojo.Books;
import java.util.List;
public class BookServiceImpl implements BookService {
//调用dao层的操作,设置一个set接口,方便Spring管理
private BookMapper bookMapper;
// 利用set进行动态 实现值的注入。
public void setBookMapper(BookMapper bookMapper) {
this.bookMapper = bookMapper;
}
public int addBook(Books book) {
return bookMapper.addBook(book);
}
public int deleteBookById(int id) {
return bookMapper.deleteBookById(id);
}
public int updateBook(Books books) {
return bookMapper.updateBook(books);
}
public Books queryBookById(int id) {
return bookMapper.queryBookById(id);
}
public List<Books> queryAllBook() {
return bookMapper.queryAllBook();
}
}
Mybatis层写完了, 即底层的 dao层 和 service层, 数据层和 业务层, 模型 Model完成了。
回忆spring:
对于spring 的重要的核心是:IOC和AOP。 控制反转和面向切面编程(利用动态代理)。IOC创建对象的方式:构造器注入、set注入、c命名和p命名注入
构造器注入:会默认直接走无参构造器。 下面的示例是直接通过参数名设置赋值。
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> </bean>
set注入:要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 ,
注意点:如果是自己定义的类,那么引入 这个值就是引入,使用 ref,而不是value
<bean id="address" class="com.AL.pojo.Address"/> <!--我们引入自定义的 Address 类--> <bean id="student" class="com.AL.pojo.Student"> <!--第一种,普通值注入,value--> <property name="name" value="小明"/> <!--第二种,Bean注入,ref--> <property name="address" ref="address"/> </bean>
p 命名空间注入:**P(属性: properties)命名空间 , 属性依然要设置set方法.**set方法,即此时的 setName 等.,在这里可以直接注入属性的值:property. 所以说 p命名法对应着 set类型注入。
<!--P(属性: properties)命名空间 , 属性依然要设置set方法 set方法,即此时的 setName 等. 在这里可以直接注入属性的值:property--> <bean id="user" class="com.AL.pojo.User" p:name="鑫鑫" p:age="18"/>
c命名空间注入: c命名法进行依赖注入,类似于构造器方法。 需要在类中创建有参构造器。
<!--C(构造: Constructor)命名空间, 通过构造器注入--> <bean id="user2" class="com.AL.pojo.User" c:name="鑫仔" c:age="18"/>
对于具体的dao层、service层的代码一种的IOC变化方式 demo:
dao层:接口和接口实现类:
package com.AL.dao; public interface UserDao { public void getUser(); }
package com.AL.dao; public class UserDaoImpl implements UserDao { public void getUser(){ System.out.println("默认用户的数据"); } }
package com.AL.dao; public class UserMysqlDaoImpl implements UserDao{ public void getUser(){ System.out.println("Mysql用户的数据"); } }
service层:接口和接口实现类
package com.AL.service; public interface UserService { public void getUser(); }
package com.AL.service; import com.AL.dao.UserMysqlDaoImpl; import com.AL.dao.UserDao; import com.AL.dao.UserDaoImpl; import com.AL.dao.UserOracleDaoImpl; public class UserServiceImpl implements UserService { /** //private UserDao userDao = new UserDaoImpl(); private UserDao userDao = new UserMysqlDaoImpl(); //private UserDao userDao = new UserOracleDaoImpl(); */ private UserDao userDao; // 利用set进行动态 实现值的注入。 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser(){ userDao.getUser(); } }
原始的 控制权交给程序猿的方式:
private UserDao userDao = new UserDaoImpl(); private UserDao userDao = new UserMysqlDaoImpl();
IOC(控制反转)的方式,将bean信息交给spring容器进行装配:【在这里,使用set注入实现IOC创建对象】
public void setUserDao(UserDao userDao) {this.userDao = userDao;}
交给spring:xml配置文件中 注入容器bean:
<bean id="userImpl" class="com.AL.dao.UserDaoImpl"/> <bean id="mysqlImpl" class="com.AL.dao.UserMysqlDaoImpl"/> <bean id="oracle" class="com.AL.dao.UserOracleDaoImpl"/> <bean id="UserServiceImpl" class="com.AL.service.UserServiceImpl"> <!-- <property name="userDao" ref="mysqlImpl"/>--> <property name="userDao" ref="oracle"/> </bean>
2.2.2、Spring层
Spring层
1、配置Spring整合MyBatis,我们这里数据源使用c3p0连接池;
2、我们去编写Spring整合Mybatis的相关的配置文件;spring-dao.xml
我们原来最开始在 mybatis中 **,需要自己在xml配置文件中引入 **sqlsessionFactoryBuild,创建一个实体工具类,然后去获得一个SqlSession,在spring中,spring层把这部分的工作完成了:
创建的Spring, 注入sqlsession到指定的包下的mapper文件:spring-dao.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置整合mybatis -->
<!-- 1.关联数据库文件 -->
<context:property-placeholder location="classpath:database.properties"/>
<!-- 2.数据库连接池 -->
<!--数据库连接池
dbcp 半自动化操作 不能自动连接
c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)
-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 配置连接池属性 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- c3p0连接池的私有属性 -->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!-- 关闭连接后不自动commit -->
<property name="autoCommitOnClose" value="false"/>
<!-- 获取连接超时时间 -->
<property name="checkoutTimeout" value="10000"/>
<!-- 当获取连接失败重试次数 -->
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!-- 3.配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
<!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 给出需要扫描Dao接口包 -->
<property name="basePackage" value="com.AL.dao"/>
</bean>
</beans>
3、Spring整合service层
spring-service.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描service相关的bean -->
<context:component-scan base-package="com.AL.service" />
<!--BookServiceImpl注入到IOC容器中-->
<bean id="BookServiceImpl" class="com.AL.service.BookServiceImpl">
<property name="bookMapper" ref="bookMapper"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
一定要将不同的context 整合起来,其中一种方式如下所示:直接导入, applicationContext.xml 配置文件。里面包括的配置信息有:
- spring-dao.xml:在这里,xml配置文件去完成 sqlSessionFactory的创建,去得到sqlSession然后对数据库表操作CRUD;且扫描dao接口
- spring-service.xml:将service层的类注册到容器 bean中,定义事务管理。【属性值注入,IOC容器】
- spring-mvc.xml:即关于springmvc中的dispatcherServlet的配置信息,定义视图解析器,开启注解驱动等。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="spring-dao.xml"/>
<import resource="spring-service.xml"/>
<import resource="spring-mvc.xml"/>
</beans>
Spring层搞定!再次理解一下,Spring就是一个大杂烩,一个容器!
在IDEA中开发时,出现的一个问题:
下面的 ref=“bookMapper” 爆红。
<!--BookServiceImpl注入到IOC容器中--> <bean id="BookServiceImpl" class="com.AL.service.BookServiceImpl"> <property name="bookMapper" ref="bookMapper"/> </bean>
这个 ref 引用的是 bookMapper,即是dao层中BookMapper(它在spring-dao.xml中注册到了spring容器:)。
爆红的原因,这说明没有把这个 bookMapper给引入进去。 解决: 在设置中 Project Structure中,把这个几个 xml配置文件添加到 spring中:
2.2.3、SpringMVC层
对于SpringMVC层, 那么就要创建一个 web,增加一个 add framework:
**1.**配置 web.xml 文件。其中包括: 围绕着DispatchServlet进行调度设计、 乱码过滤。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--DispatcherServlet-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--一定要注意:我们这里加载的是总的配置文件,之前被这里坑了!-->
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--encodingFilter-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--Session过期时间-->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
</web-app>
2、spring-mvc.xml。 包括映射器、适配器、视图解析器。开启注解驱动
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置SpringMVC -->
<!-- 1.开启SpringMVC注解驱动 -->
<mvc:annotation-driven />
<!-- 2.静态资源默认servlet配置-->
<mvc:default-servlet-handler/>
<!-- 3.配置jsp 显示ViewResolver视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 4.扫描web相关的bean -->
<context:component-scan base-package="com.AL.controller" />
</beans>
3、Spring配置整合文件,applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:spring-dao.xml"/>
<import resource="classpath:spring-service.xml"/>
<import resource="classpath:spring-mvx.xml"/>
</beans>
配置文件,暂时结束!Controller 和 视图层编写
2.2.4、Controller 和 视图层编写
方法一:查询全部书籍
1、BookController 类编写 ,
1.定义一个 BookController, 用于去调用 service层:
package com.AL.controller;
import com.AL.pojo.Books;
import com.AL.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/book")
public class BookController {
@Autowired
@Qualifier("BookServiceImpl")
private BookService bookService;
//查询全部的书籍, 并且返回到一个书籍展示页面
@RequestMapping("/allBook")
public String list(Model model) {
List<Books> list = bookService.queryAllBook();
model.addAttribute("list", list);
return "allBook";
}
}
@Autowired 和 @Qualifier注解:
在spring中学习时关于使用注解实现自动装配bean的方式:@Autowired、@Qualifier、@Resource。然后在xml配置中开启注解,即可完成向spring容器中自动装配bean,并且能够获取。
@Autowired 等价于, 名字需要符号 byName
@Qualifier适用于当容器 beans.xml中有多个属性类型一样,但 id 名字不一样的,针对特定对象的:
@Autowired @Qualifier(value = "cat2") private Cat cat;
<bean id="cat" class="com.AL.pojo.Cat"/> <bean id="cat2" class="com.AL.pojo.Cat"/>
2.需要跳转的 allBook层的页面:【显示书籍信息】
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>书籍列表</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>书籍列表 —— 显示所有书籍</small>
</h1>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
</div>
<div class="col-md-4 column"></div>
<div class="col-md-4 column">
<%--查询书籍--%>
<form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
<span style="color: blue; font-weight: bold">${error}</span>
<input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
</div>
</div>
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>书籍编号</th>
<th>书籍名字</th>
<th>书籍数量</th>
<th>书籍详情</th>
</tr>
</thead>
<%--书籍从数据库中查询出来。然后遍历这个List得到书籍信息:foreach--%>
<tbody>
<c:forEach var="book" items="${requestScope.get('list')}">
<tr>
<td>${book.getBookID()}</td>
<td>${book.getBookName()}</td>
<td>${book.getBookCounts()}</td>
<td>${book.getDetail()}</td>
<td>
<a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> |
<a href="${pageContext.request.contextPath}/book/deleteBook/${book.getBookID()}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
3.对于默认页面的一个修改, 让其 url路径直接变为我们 controlle的路径, 然后就能进行跳转:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h3>
<a href="${pageContext.request.contextPath}/book/allBook">进入书籍页面</a>
</h3>
</body>
</html>
启动Tmocat,进行测试:查看能否运行:
测试:进行排错的步骤:
- 查看这个bean注入是否成功
- 使用Junit单元测试。查看代码获取bean后 是否能够查询出结果
- 能够正常查询的话。说明问题不在底层,出现在了 spring层。就检查 springMVC中问题,是否能够调用bean。
- applicationContext.xml中有没有注入bean
- web.xml中绑定配置文件,DispatchServlet时配置的是不是 关于service层的xml配置文件?
import com.AL.pojo.Books;
import com.AL.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookServiceImpl = (BookService) context.getBean("BookServiceImpl");
for (Books books : bookServiceImpl.queryAllBook()) {
System.out.println(books);
}
}
}
能够在 java IDEA中输出结果:表明我们的代码没有问题。能够在后台进行调用。
对默认的初始化页面进行修改:显示的色彩,字体的修改 居中,文本格式
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
<style type="text/css">
a{
text-decoration: none;
color: black;
font-size: 18px;
}
h3 {
width: 180px;
height: 38px;
margin: 100px auto;
text-align: center;
line-height: 38px;
background: deepskyblue;
border-radius: 5px;
}
</style>
</head>
<body>
<h3>
<a href="${pageContext.request.contextPath}/book/allBook">进入书籍页面</a>
</h3>
</body>
</html>
结果如下所示:
对于书籍展示页面: 导入bookstrap, 对 book list 进行展开:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>书籍列表</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>书籍列表 —— 显示所有书籍</small>
</h1>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
</div>
<div class="col-md-4 column"></div>
<div class="col-md-4 column">
<%--查询书籍--%>
<form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
<span style="color: blue; font-weight: bold">${error}</span>
<input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
</div>
</div>
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>书籍编号</th>
<th>书籍名字</th>
<th>书籍数量</th>
<th>书籍详情</th>
</tr>
</thead>
<%--书籍从数据库中查询出来。然后遍历这个List得到书籍信息:foreach--%>
<tbody>
<c:forEach var="book" items="${requestScope.get('list')}">
<tr>
<td>${book.getBookID()}</td>
<td>${book.getBookName()}</td>
<td>${book.getBookCounts()}</td>
<td>${book.getDetail()}</td>
<td>
<a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> |
<a href="${pageContext.request.contextPath}/book/deleteBook/${book.getBookID()}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
修改后的结果:
方法二:添加书籍功能
跳转到添加书籍页面:
- 在进入所有书籍展示页面, 需要提供一个 按钮, 对应功能为跳转到 添加书籍页面, 对应的url为 /toAddBook, 调用toAddPaper方法,进行跳转:
<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
</div>
</div>
- 在controller中的代码部分: 增加收到用户此请求后,跳转到增加书籍信息的页面:
//跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddPaper() {
return "addBook";
}
-
定义添加书籍的页面:当添加书籍页面进行添加的时候, controll需要相应添加这个事件:在添加页面,实行添加动作后,指定跳转页面为 /book/addBook,方法这里选择 post。 注意:这里的名字要对应类中的属性,这样才能添加成功。
响应动作值value=“添加”,然后跳转:action=“${pageContext.request.contextPath}/book/addBook” method=“post”
在添加的时候,可以定义 required,这样就必须有内容,才能提交:
<div class="form-group">
<label>书籍名称:</label>
<input type="text" name="bookName" class="form-control" required>
</div>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>新增书籍</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>新增书籍</small>
</h1>
</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/book/addBook" method="post">
<div class="form-group">
<label>书籍名称:</label>
<input type="text" name="bookName" class="form-control" required>
</div>
<div class="form-group">
<label>书籍数量:</label>
<input type="text" name="bookCounts" class="form-control" required>
</div>
<div class="form-group">
<label>书籍描述:</label>
<input type="text" name="detail" class="form-control" required>
</div>
<div class="form-group">
<input type="submit" class="form-control" value="添加" required>
</div>
</form>
</div>
**相应添加事件,**url路径来到 /book/addBook 这里对应的 controller,去调用的方法, **添加完成后,重定向到开始的所有书籍页面:**此时目前的controller中关于添加书籍的代码为:
//跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddPaper() {
return "addBook";
}
// 添加书籍的请求
@RequestMapping("/addBook")
public String addPaper(Books books) {
System.out.println(books);
bookService.addBook(books);
return "redirect:/book/allBook"; //重定向到我们的@RequestMapping("/allBook")请求
}
成功添加书籍,并返回到 重定向到 展示所有书籍界面:
再次捋一下思路:
- 在所有书籍显示页面,增加新增书籍按钮。绑定新增书籍按钮点击提交的路径:新增书籍
- /book/toaddBook 路径请求转发到 addBook.jsp页面,在这里进行添加书籍。 表单提交,将书籍属性传递给 books,采用的映射,字段和属性对应。【参数的数据传递】
- 添加成功后重定向到 redirect:/book/allBook 所有书籍显示页面。
方法三:修改书籍
在页面添加修改和删除的选项:
点击时,controller得到请求,然后让其响应跳转到修改或删除的指定页面,
在这个页面进行修改和删除, 再选择提交,指定的按钮 动作事件,发送请求再重定向到 所有书籍页面。
对于展示所有书籍页面的按钮,在前面已经增加过了。
controller代码修改: 增加跳转到 修改书籍信息页面, 以及书籍信息页面修改后的重定向:
//跳转到修改页面
@RequestMapping("/toUpdateBook")
public String toUpdateBook(Model model, int id) {
Books books = bookService.queryBookById(id);
System.out.println(books);
model.addAttribute("QBook",books);
return "updateBook";
}
//修改书籍的请求
@RequestMapping("/updateBook")
public String updateBook(Model model, Books books) {
System.out.println("updateBook"+ books );
bookService.updateBook(books);
return "redirect:/book/allBook";
}
在这里需要设置修改书籍页面的信息:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>修改信息</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>修改书籍</small>
</h1>
</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/book/updateBook" method="post">
<div class="form-group">
<label>书籍名称:</label>
<input type="text" name="bookName" class="form-control" value="${QBook.bookName}" required>
</div>
<div class="form-group">
<label>书籍数量:</label>
<input type="text" name="bookCounts" class="form-control" value="${QBook.bookCounts}" required>
</div>
<div class="form-group">
<label>书籍描述:</label>
<input type="text" name="detail" class="form-control" value="${QBook.detail}" required>
</div>
<div class="form-group">
<input type="submit" class="form-control" value="修改" required>
</div>
</form>
</div>
结果:有问题, 没有覆盖掉 。 添加事务后成功。
有问题,是不是因为事务没有提交的原因。
添加AOP 面向切面编程:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8.M1</version>
</dependency>
在spring-service.xml配置文件中已经写过了。
方法四:删除书籍
删除书籍,对于这个用户请求,直接重定向到所有书籍展示的页面即可, 不用定义一个删除界面
删除书籍对应的 跳转路径 url 请求:
<a href="${pageContext.request.contextPath}/book/deleteBook/${book.getBookID()}">删除</a>
controller页面:
// 删除书籍
@RequestMapping("/deleteBook/{bookId}")
public String deleteBook(@PathVariable("bookId") int id) {
bookService.deleteBookById(id);
return "redirect:/book/allBook";
}
方法五:新增搜索功能
前端所有书籍页面:
增加查询功能:
<div class="col-md-4 column">
<%--查询书籍--%>
<form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
<span style="color: blue; font-weight: bold">${error}</span>
<input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
</div>
在进行代码修改的时候, 从下往上进行:
对于dao层:
- 接口
// 根据名字查询书籍信息
Books queryBookByName(String bookName);
- 接口实现类: sql语句,注册绑定:
<select id="queryBookByName" resultType="Books">
select * from ssmbuild.books where bookName = #{bookName}
</select>
service层也是同样的事情:
- 接口:
// 根据名字查询书籍信息
Books queryBookByName(String bookName);
- 接口实现类:
public Books queryBookByName(String bookName){
return bookMapper.queryBookByName(bookName);
}
Controller 代码 响应请求调用 servlet:
//查询书籍
@RequestMapping("/queryBook")
public String queryBook(String queryBookName, Model model){
Books books = bookService.queryBookByName(queryBookName);
System.out.println("queryBook=>"+books);
List<Books> list = new ArrayList<Books>();
list.add(books);
if (books==null){
list = bookService.queryAllBook();
model.addAttribute("error","未查到");
}
model.addAttribute("list", list);
return "allBook";
}
添加一个功能:显示全部书籍
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
想要收到错误信息,在前端页面:
- 获取 error信息展示
<%--查询书籍--%>
<form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
<span style="color: blue; font-weight: bold">${error}</span>
<input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
总结
总的代码和流程分析
项目结构图:
从下往上进行修改,先写底层,
配置文件解析
database.properties
-
database.properties:数据库资源配置文件
- driver:数据库驱动
- url:数据库表 ssmbuild 的url地址
- username:数据库管理员的名字;password:数据库管理员的密码
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=123456
mybatis-config.xml
-
mybatis-config.xml:mybatis配置文件,
- 定义关于数据库表和对应的java的pojo类 字段
- 起别名
- dao接口的绑定、dao接口实现类的绑定
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <package name="com.AL.pojo"/> </typeAliases> <mappers> <mapper resource="com/AL/dao/BookMapper.xml"/> </mappers> </configuration>
-
原来学习的:mybatis-config.xml配置文件
对于mybatis学习中更加完整的一个: 因为直接接触dao层了吗?所以直接添加了数据库的环境配置信息 enviroments。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--XML 配置文件或者预先定义的一个 config,去SqlSessionFactoryBuilder, 配置数据库,便于创建sqlSessionFactory实例--> <configuration> <!-- <properties resource="db.properties">--> <!-- <!– 在 db.properties 属性配置文件中定义了这些 sql 配置,可以不在这里的xml配置文件中定义。--> <!-- 如果定义的话, 会以 xml资源配置文件中的 (引入外部配置文件) 为基准,优先级比较高--> <!-- <property name="username" value="root"/>--> <!-- <property name="pwd" value="111"/>--> <!-- –>--> <!-- </properties>--> <properties resource="db.properties" /> <!--可以给实体类起别名--> <typeAliases> <typeAlias type="com.AL.pojo.User" alias="User"/> </typeAliases> <!-- <typeAliases> <package name="com.AL.pojo"/> </typeAliases>--> <environments default="development"> <!-- 定义默认的环境配置,使用default选择环境 development--> <environment id="development"> <transactionManager type="JDBC"/> <!--事务管理器--> <dataSource type="POOLED"> <!--数据源、默认的连接池--> <!-- <property name="driver" value="com.mysql.jdbc.Driver"/>--> <!-- <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charsetEncoding=UTF-8"/>--> <!-- <property name="username" value="root"/>--> <!-- <property name="password" value="123456"/>--> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <!-- <property name="url" value="jdbc:mysql://localhost:3306/jdbc"/>--> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC"/> <!--事务管理器--> <dataSource type="POOLED"> <!--默认的连接池--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&charsetEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--关联映射文件--> <mappers> <mapper resource="com/AL/dao/UserMapper.xml"/> <!--绑定接口--> <mapper class="com.AL.dao.UserMapper"/> </mappers> </configuration>
spring-dao.xml:spring整合mybatis
-
spring-dao.xml层:和dao层 底层数据库连接操作的配置文件
- spring配置整合mybatis。此时的配置文件包括的内容有:
- 关联数据库文件,连接数据库表
- 配置数据库连接池。例如dbcp、c3p0的。 可以直接从数据库表的配置文件中读取 value=“${jdbc.driver}”
- 配置SqlSessionFactory对象。利用SqlSessionFactory去创建一个SqlSession 用于去完成对dao层数据库的操作
- 注入sqlSessionFactory 以及 注入 Dao接口。
- 原来的mybatis-config,xml 配置文件,mybatis中需要从资源文件利用SqlSessionFactoryBuild来得到SqlSessionFactory实例化对象,使用SqlSessionFactory来新建sqlsession。【创建一个实体工具类,然后去获得一个SqlSession,在spring中,spring层把这部分的工作完成了】
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置整合mybatis --> <!-- 1.关联数据库文件 --> <context:property-placeholder location="classpath:database.properties"/> <!-- 2.数据库连接池 --> <!--数据库连接池 dbcp 半自动化操作 不能自动连接 c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面) --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 配置连接池属性 --> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- c3p0连接池的私有属性 --> <property name="maxPoolSize" value="30"/> <property name="minPoolSize" value="10"/> <!-- 关闭连接后不自动commit --> <property name="autoCommitOnClose" value="false"/> <!-- 获取连接超时时间 --> <property name="checkoutTimeout" value="10000"/> <!-- 当获取连接失败重试次数 --> <property name="acquireRetryAttempts" value="2"/> </bean> <!-- 3.配置SqlSessionFactory对象 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据库连接池 --> <property name="dataSource" ref="dataSource"/> <!-- 配置MyBatis全局配置文件:mybatis-config.xml --> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> <!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 --> <!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 注入sqlSessionFactory --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!-- 给出需要扫描Dao接口包 --> <property name="basePackage" value="com.AL.dao"/> </bean> </beans>
- spring配置整合mybatis。此时的配置文件包括的内容有:
-
对于原来学习的:单独的一个配置,联合 mybatis的xml文件。
spring-10mybatis这个maven项目中的spring-dao.xml文件内容如下:
- 原来的mybatis-config,xml 配置文件,mybatis中需要从资源文件利用SqlSessionFactoryBuild来得到SqlSessionFactory实例化对象,使用SqlSessionFactory来新建sqlsession。【创建一个实体工具类,然后去获得一个SqlSession,在spring中,spring层把这部分的工作完成了】
- 使用了sqlSessionTemplate 去创建一个 sqlSession
- 另外一种,利用SqlSessionDaoSupport 类 去创建得到的sqlSession
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--配置数据源:数据源有非常多,可以使用第三方的, 如: c3p0 dbcp druid 也可使使用Spring的:org.springframework.jdbc.datasource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!--配置SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--关联Mybatis--> <property name="configLocation" value="classpath:mybatis -config.xml"/> <property name="mapperLocations" value="classpath:com/AL/mapper/*.xml"/> </bean> <!--注册sqlSessionTemplate: 就是我们使用的 sqlSession. 关联sqlSessionFactory--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--利用构造器注入 sqlSessionFactory, 因为它没有 set 方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <bean id="userMapper" class="com.AL.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> <bean id="userMapper2" class="com.AL.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <!-- 配置声明式事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!--配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--配置哪些方法使用什么样的事务,配置事务的传播特性--> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="search*" propagation="REQUIRED"/> <tx:method name="get" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--配置aop织入事务--> <aop:config> <aop:pointcut id="txPointcut" expression="execution(* com.AL.dao.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config> </beans>
两个spring-dao.xml文件的比较:
-
<!-- 3.配置SqlSessionFactory对象 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据库连接池 --> <property name="dataSource" ref="dataSource"/> <!-- 配置MyBatis全局配置文件:mybatis-config.xml --> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> <!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 --> <!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 注入sqlSessionFactory --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!-- 给出需要扫描Dao接口包 --> <property name="basePackage" value="com.AL.dao"/> </bean>
-
<!--配置SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--关联Mybatis--> <property name="configLocation" value="classpath:mybatis -config.xml"/> <property name="mapperLocations" value="classpath:com/AL/mapper/*.xml"/> </bean> <!--注册sqlSessionTemplate: 就是我们使用的 sqlSession. 关联sqlSessionFactory--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--利用构造器注入 sqlSessionFactory, 因为它没有 set 方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <bean id="userMapper" class="com.AL.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> <bean id="userMapper2" class="com.AL.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
其实这两个配置文件的作用是一样的。 都要使用SqlSessionFactory 去新建一个sqlSession,完成dao层接口、接口实现类的工作,对数据库操作。
Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring
Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring
Mybatis在与Spring集成的时候可以配置MapperFactoryBean来生成Mapper接口的代理. 例如
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
MapperFactoryBean 创建的代理类实现了 UserMapper 接口,并且注入到应用程序中。 因为代理创建在运行时环境中(Runtime,译者注) ,那么指定的映射器必须是一个接口,而 不是一个具体的实现类。
上面的配置有一个很大的缺点,就是系统有很多的配置文件时 全部需要手动编写,所以上述的方式已经很用了。
没有必要在 Spring 的 XML 配置文件中注册所有的映射器。相反,你可以使用一个 MapperScannerConfigurer , 它 将 会 查 找 类 路 径 下 的 映 射 器 并 自 动 将 它 们 创 建 成 MapperFactoryBean。
要创建 MapperScannerConfigurer,可以在 Spring 的配置中添加如下代码:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean>
basePackage 属性是让你为映射器接口文件设置基本的包路径。 你可以使用分号或逗号 作为分隔符设置多于一个的包路径。每个映射器将会在指定的包路径中递归地被搜索到。
注 意 , 没 有 必 要 去 指 定 SqlSessionFactory 或 SqlSessionTemplate , 因 为 MapperScannerConfigurer 将会创建 MapperFactoryBean,之后自动装配。但是,如果你使 用了一个 以上的 DataSource ,那 么自动 装配可 能会失效 。这种 情况下 ,你可 以使用 sqlSessionFactoryBeanName 或 sqlSessionTemplateBeanName 属性来设置正确的 bean 名 称来使用。这就是它如何来配置的,注意 bean 的名称是必须的,而不是 bean 的引用,因 此,value 属性在这里替代通常的 ref:
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
MapperScannerConfigurer 支 持 过 滤 由 指 定 的 创 建 接 口 或 注 解 创 建 映 射 器 。 annotationClass 属性指定了要寻找的注解名称。 markerInterface 属性指定了要寻找的父 接口。如果两者都被指定了,加入到接口中的映射器会匹配两种标准。默认情况下,这两个 属性都是 null,所以在基包中给定的所有接口可以作为映射器加载。
被发现的映射器将会使用 Spring 对自动侦测组件(参考 Spring 手册的 3.14.4)默认的命 名策略来命名。也就是说,如果没有发现注解,它就会使用映射器的非大写的非完全限定类 名。但是如果发现了@Component 或 JSR-330 的@Named 注解,它会获取名称。注意你可以 配 置 到 org.springframework.stereotype.Component , javax.inject.Named(如果你使用 JSE 6 的话)或你自己的注解(肯定是自我注解)中,这 样注解将会用作生成器和名称提供器。
接下来让我们看一下MapperScannerConfigurer类的源码 看看是如何自动扫描的。
1 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
2 if (this.processPropertyPlaceHolders) {
3 processPropertyPlaceHolders();
4 }
5
6 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
7 scanner.setAddToConfig(this.addToConfig);
8 scanner.setAnnotationClass(this.annotationClass);
9 scanner.setMarkerInterface(this.markerInterface);
10 scanner.setSqlSessionFactory(this.sqlSessionFactory);
11 scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
12 scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
13 scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
14 scanner.setResourceLoader(this.applicationContext);
15 scanner.setBeanNameGenerator(this.nameGenerator);
16 scanner.registerFilters();
17 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
18 }
把Mapper接口转换成MapperFactoryBean的代码在地17行这个方法里,让我们跟踪进去看一下。
1 @Override
2 public Set<BeanDefinitionHolder> doScan(String... basePackages) {
3 Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
4
5 if (beanDefinitions.isEmpty()) {
6 logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
7 } else {
8 for (BeanDefinitionHolder holder : beanDefinitions) {
9 GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
10
11 if (logger.isDebugEnabled()) {
12 logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
13 + "' and '" + definition.getBeanClassName() + "' mapperInterface");
14 }
15
16 // the mapper interface is the original class of the bean
17 // but, the actual class of the bean is MapperFactoryBean
18 //把接口的类型设置进去
19 definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
20 //设置Bean的真实类型MapperFactoryBean
21 definition.setBeanClass(MapperFactoryBean.class);
22 //是否把Mapper接口加入到Mybatis的Config当中去
23 definition.getPropertyValues().add("addToConfig", this.addToConfig);
24
25 boolean explicitFactoryUsed = false;
26 //如果sqlSessionFactoryBeanName的名字不为空 则在Spring容器中查询
27 //适合多数据源
28 if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
29 definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
30 explicitFactoryUsed = true;
31 } else if (this.sqlSessionFactory != null) {
32 definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
33 explicitFactoryUsed = true;
34 }
35
36 //如果sqlSessionTemplateBeanName的名字不为空 则在Spring容器中查询
37 //适合多数据源
38 if (StringUtils.ha
39 if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
40 if (explicitFactoryUsed) {
41 logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
42 }
43 definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
44 explicitFactoryUsed = true;
45 } else if (this.sqlSessionTemplate != null) {
46 if (explicitFactoryUsed) {
47 logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
48 }
49 definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
50 explicitFactoryUsed = true;
51 }
52
53 if (!explicitFactoryUsed) {
54 if (logger.isDebugEnabled()) {
55 logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
56 }
57 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
58 }
59 }
60 }
61 //这个集合返回以后 Spring容器会将里面的所有内容注册到容器中
62 return beanDefinitions;
63 }
spring-service.xml
spring-service.xml配置文件,关于service层的:
- 将 service中的相关bean注册到spring容器中
- service层中的bean注入。使用了set注入方式:
- 事务管理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描service相关的bean -->
<context:component-scan base-package="com.AL.service"/>
<!--BookServiceImpl注入到IOC容器中-->
<bean id="BookServiceImpl" class="com.AL.service.BookServiceImpl">
<property name="bookMapper" ref="bookMapper"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
在上述的spring中相关的几个xml配置可以看出,将dao层、service层里面的bean都注入到了IOC容器中,到时需要的时候直接从 spring容器中获取即可。