SSM完整笔记
- Mybatis-01-配置详解
- Mybatis-02-日志,注解和分页,一对多,多对一,缓存
- Spring详解-01-依赖注入等方式
- Spring详解-02-注解,面向切面编程等
- Spring详解-03-整合Mybatis,事务配置
- SpringMVC-01-第一个SpringMVC程序,注解开发,Restful风格
- SpringMVC-02-Ajax,拦截器,文件上传下载
- SSM项目整合-完整流程一览
- 笔记相关代码:https://gitee.com/chelsea_h/ssm-integration
官网文档
- Spring文档:https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/core.html#spring-core
- Mybatis文档:http://www.mybatis.org/mybatis-3/zh/index.html
- SpringMVC文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
一:为什么是Mybatis
- 什么是Mybatis:Mybatis是一款优秀的持久层框架,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的过程。可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的实体类映射成数据库中的记录。
- 持久化:持久化是将程序数据在持久状态和顺势状态间转换的机制(因为内存空间小且断电失效,所以必须存在硬盘中)。
- Mybatis就是帮程序员在数据库中存取数据,传统的JDBC有很多重复代码,通过框架可以减少重复代码,提高开发效率。
- Mybatis优点:简单易学,灵活(sql写在xml里,便于统一管理和优化)
二:第一个Mybatis程序
- 搭建数据库(本项目使用MySql数据库)
CREATE DATABASE `mybatis`;
USE `mybatis`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`name`,`pwd`) values (1,'王二','123456'),(2,'张三','abcdef'),(3,'李四','987654');
- Maven项目导入相关依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
- 编写Mybatis核心配置文件,命名为Mybatis-config.xml文件,当然文件名可以自己修改,只需要在MybatisUtils类中加载的时候选择对应的名称即可。
<?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>
<environments 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&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/kuang/dao/userMapper.xml"/>
</mappers>
</configuration>
- 编写Mybatis工具类(可以当作自己的一个工具,每次调用数据就获取sqlSession,当作数据库连接池中的连接对象使用)
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
/*固定的三步,读取xml文件,从xml文件中构建sqlSessionFactory*/
static {
try {
String resourse = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resourse);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/*从sqlSessionFactory中获取sqlSession实例
* sqlSession完全包含了面向数据库执行SQL命令所需的所有方法
* */
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
- 测试
编写实体类和Mapper接口类
//这三个注解时lombok的,需要在idea中安装lombok插件,在项目中引入lombok依赖才能使用
//Data注解不需要手动写get,set,toString等方法
//AllArgsConstructor注解声明一个全部参数的构造器
//NoArgsConstructor注解声明一个无参构造器
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
public interface UserMapper {
/*获取全部用户*/
List<User> getUserList();
}
- 编写Mapper.xml配置文件,namespace非常重要,一定要对应到对应的接口文件中,并且每写一个Mapper文件,就要在Mybatis-config.xml文件Mappers标签下配置一个mapper文件路径(有三种配置方式)。
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.chelsea.dao.UserMapper">
<!--select查询语句-->
<!--id就相当于对应namespace中的方法名-->
<!--resultType:sql语句中的返回值-->
<select id="getUserList" resultType="com.com.chelsea.pojo.User">
/*执行SQL语句*/
select * from user
</select>
</mapper>
- 测试
public class UserMapperTest {
@Test
public void test(){
// 第一步:获得sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 第二步:方式一:使用Mapper映射方式执行sql
//UserDao mapper = sqlSession.getMapper(UserDao.class);
//List<User> userList = mapper.getUserList();
// 第二步:方式二:不推荐使用
List<User> userList = sqlSession.selectList("com.chelsea.dao.UserMapper.getUserList");
for (User user:userList){
System.out.println(user);
}
//关闭sqlSession
sqlSession.close();
}
}
- 可能出现的问题:静态资源过滤问题。如果UserMapper.xml文件没有写在Resource文件夹下,maven项目找不到此静态资源,则需要在pom文件中配置静态资源过滤问题,使得在java文件目录下的静态资源也可以被索引到。
<!--在build中配置resources,来防止我们资源导出失败的原因-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
三:Mybatis XML配置详解
Mybatis的第一个程序已经搭建完毕,其他的增删改查操作可根据情况自己编写,后续讲述其他部分内容。
- 引入外部配置文件,为了方便配置文件的管理,可以将数据库驱动,用户名密码,url等单独提出了,写在db.properties文件中,内容大致如下,之后在Mybatis的配置文件中引入此db.properties文件内容。(Mybatis中默认的事务管理器时JDBC,连接池默认为POOLED,除此之外还有c3p0等)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
<!--可以同时在mybatis配置文件和db.properties文件中配置数据源信息,如果同一个信息两处都配置,优先读取db.properties文件中信息。-->
<properties resource="db.properties">
<property name="username" value="chelsea"/>
<property name="password" value="han990713"/>
</properties>
- 引入别名
- 别名是为Java类型设置一个短的名字,存在的意义仅在于用来减少类完全限定名的冗余
- 两种方式:一种是直接为一个类写一个别名,另一种是可以直接扫描包
- 其实还有第三种方式,通过注解来进行增加别名。在Myabtis的配置文件中配置别名信息如下,配置完成后在Mapper.xml文件中就可以不用使用全限定名:
<!--配置别名-->
<typeAliases>
<!--第一种方式:为每个类进行别名配置-->
<typeAlias type="com.chelsea.pojo.User" alias="user"/>
<!--第二种方式:配置扫描包,在没有注解的情况下(优先使用注解),扫描包下的所有bean,默认为类名,首字母小写-->
<package name="com.chelsea.pojo"/>
</typeAliases>
- 生命周期作用域
- SqlSessionFactoryBuilder:一旦创建就不需要了,是一个局部变量
- SqlSessionFactory:可以类似于数据库连接池,一旦创建就应该在应用运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 因此SqlSessioFactory的最佳作用域时应用作用域,最简单的使用就是单例模式或者静态单例模式
- SqlSession:连接到连接池的一个请求,不是线程安全的,因此不能被共享,所以他的最佳作用域是请求或方法作用域,用完之后需要赶紧关闭,否则资源被占用。
- 对应的代码如下:
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
/*固定的三步,读取xml文件,从xml文件中构建sqlSessionFactory*/
static {
try {
String resourse = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resourse);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/*从sqlSessionFactory中获取sqlSession实例
* sqlSession完全包含了面向数据库执行SQL命令所需的所有方法
* */
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
- ResultMap标签
- 当属性名和字段名不一致时,查询结果可能为空:User{id=2, name=‘lovecd’, password=‘null’}。因为User属性为password,而非pwd,所以查询结果为空(sql语句为:select id,name,pwd from user where id = #{id})。
- 解决方案1,起别名:select id,name,pwd as password from user where id = #{id}
- 解决方案2,使用ResultMap映射:将数据库中的pwd映射为password,还有其他更奇葩的映射,可以放下面看一下。
<resultMap id="user" type="com.chelsea.pojo.User">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="password" column="pwd"/>
</resultMap>
- 对于简单的语句根本不需要配置显示的结果映射,对于复杂一点的语句只需要描述他们的关系即可
- 有时需要联表查询,就需要resultMap进行映射,举例如下:
<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>