文章目录
前言
1、web项目的三层架构:
①界面层:用于接受用户的数据,返回数据的处理结果
②业务处理层:用来计算处理用户的数据,调用数据库
③持久层(数据访问层):连接数据库进行增删改查(CRUD)
2、层级关系:用户使用界面===>业务处理层===>数据访问层===>数据库
3、为了简便程序的开发,每一层都有对应的实现框架
界面层:springmvc,
数据访问层:spring,主要技术ioc,aop
持久层:mybatis,动态sql,传参,返回值类型
一、mybatis概述
一个简化数据库增删改查操作的,十分实用的框架,而且还可以与spring,springmvc等框架整合使用,降低了程序的开发难度和减少了代码的冗余
二、利用Maven构建一个简单的mybatis实现(IDEA)
1.在IDEA中配置maven工具
2.新建java项目
在新建的项目中选中maven,再选中archetype,勾选quickstart,构架一个java项目(因为只是一个简单的测试mybatis的,不需要构建web项目),然后修改pom文件的内容,需要导入mybatis的依赖,和对应JDBC的依赖,还需要能够加载映射文件的插件。
< groupId>org.mybatis< /groupId>
< artifactId>mybatis< /artifactId>
< version>3.5.1< /version>
<
< groupId>mysql< /groupId>
< artifactId>mysql-connector-java< /artifactId>
< version>8.0.13< /sersion>//如果是mysql5的话,那么使用5.0+的版本
<
< build>
< resources>
< resource>
< directory>src/main/java< /directory>
表示在什么样的目录下的什么文件需要拷贝到classes目录下
< include>/*.properties< /include>
< include>/*.xml< /include>
< /includes>
< filtering>false< /filtering>
< /resource>
< /resources>
< /build>
3.新建接口cn.edu.hbpu.dao.StudentDao和sql配置文件
StudentDao.java
public interface StudentDao {
List<Student> selectStudents();
int insertStudents(Student student);
}
StudentDao.xml
<?xml version="1.0" encoding="UTF-8" ?>PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
< mapper namespace=“com.test.dao.StudentDao”>
< select id=“selectStudents” resultType=“com.test.domain.Student”>
select * from student
< /select>
< insert id=“insertStudents”>
insert into student values(#{id},#{name},#{email},#{age})
< /insert>
< /mapper>
SQL映射文件标签解读:
1)namespace
标识当前sql映射文件的命名空间,赋值dao层中的接口的全限定名称,
2)select/insert/update/delete
eg:<insert id="唯一标识,不能重复与接口中的方法名对应"><insert/>
4.创建mybatis主配置文件
在resoruces文件夹下面创建mybatis.xml文件
<?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><!--当前xml文件的根标签-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"></setting>
</settings>
<environments default="development"><!--数据库的配置信息,default的值必须和一个具体的数据库的id值相同-->
<environment id="development"><!--一个具体的数据库的配置信息-->
<transactionManager type="JDBC"/><!--当前使用的事务类型,基本上都是JDBC(也就是Connection对象的commit,rollback的事务类型)-->
<dataSource type="POOLED"><!--数据源,都是使用的POOLED(连接池)-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/><!--具体的连接信息-->
<property name="url" value="jdbc:mysql://localhost:3306/student?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="****"/>
<property name="password" value="****"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/test/dao/mapper.xml"/>
<!-- 可以指定多个mapper-->
</mappers>
</configuration>
主配置文件解读:
1、settings标签标识在控制台输出mybatis执行的过程
2、mappers标签指定sql映射文件的位置,如果mapper指定,路径的分隔符不能使用.
5.创建测试类
public class testInsert {
@Test
public void insertStudents() throws IOException {
String config = "main.xml";
//2.读取这个主配置文件
InputStream in = Resources.getResourceAsStream(config);
//3.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//4.创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);
//5.获取SqlSession,从SqlSessionFactory中获取
SqlSession sqlSession = factory.openSession();
//6.指定要执行的sql语句的表示,也就是sql映射文件中的namespace+"."+标签的id值
String sqlId = "com.test.mybatis01.dao.StudentDao"+"."+"insertStudents";
//7.执行sql语句
int res = sqlSession.insert(sqlId,new Student(1004,"张飞","432@sina.com",15));
sqlSession.commit();
//8.输出/处理结果
System.out.println("影响数据库的记录条数"+res);
//9.关闭sqlSession对象
sqlSession.close();
}
}
1、创建sqlSession对象默认是不自动提交事务的。需要自动手动提交(适合事务),或者设置自动提交(适合单表操作或者查询操作)。SqlSessionFactory.openSession(true)
2、sql映射文件中的sql语句必须与接口中的方法保持一致
3、我们也可以使用SqlSession中的getMapper(Class classz),这样可以对应dao接口的实现类,不用我们自己创建接口的实现类,在完成数据库操作之后,记得关闭sqlSession对象
三、参数类型
也就是sql映射文件中sql语句中的parameterType标签:因为可以通过反射机制获取到参数的类型,所以不需要我们指定。
1、一个参数:
简单参数类型直接传参即可,在sql语句中使用#{参数名}进行调用,实测:其实对于只有一个参数的情况来说,参数名可以是任意的,但是如果使用${}占位符进行调用那么必须和参数必须加上注解才能使用
2、多个参数解决方法:
①使用注解:@param(“id”),使用#/${}可以直接调用
②对象,将所有的属性封装在一个类中,使用#/${属性名}即可调用,(完整的是#{属性名,javaType=“java中数据类型”,jdbcType=“数据库中数据类型”})
③使用map,将参数信息以键值对的方式存储在map集合中,使用#/${属性名}即可调用
④使用list/array,与后面的动态sql中foreach语句有关
⑤使用参数的索引,在mybatis3.3之前使用#{0/1/2},在mybatis3.3(包括)之后使用#{arg0/1/2}
※$和#的区别:
#:使用这个占位符,mybatis会使用PreparedStatement对象预编译sql语句,然后执行sql,这样可以达到一次编译,多次执行的效果,效率搞,而且sql语句中使用?占位符作为参数。可以防止sql注入现象
$:使用这个占位符,mybatis会使用Statement对象,执行sql,对于sql语句完全是字符串的拼接操作,这样会导致sql注入现象,eg:sql = "select * from student where id = +"1 or 1 = 1"";但是如果只是代表一些与数据无关的字段名/表名
四、返回值类型
如果我们在使用select语句,那么可能只有一个记录(max,min),也可能只有一条记录,或者多条记录,这时候就需要我们指定返回值的类型的。
1、如果只有一个记录值(假如是int类型),那么我们就只需要使用java.lang.Integer作为resultType的值,也可以直接使用int(Integer的别名),
2、多行或者一行:
①可以将数据存储在一个实体类中的,这时也需要写类的全限定名称(我们也可以为类取别名,在mybatis主配置文件中)
<typeAliases>
<typeAlias type="com.test.domain.Student" alias="studnet"/>
<package name="com.test"></package>
</typeAliases>
第一条语句代表为指定类取别名,第二条代表为包下面的所有类取别名,不区别大小写,我们也可以使用@Alias注解自定义类名,即在resultType中可以写student,也可以是sTudenT,值得注意的是,字段名和属性名同名,因为mybatis是调用set+字段名(不区分大小写)的方法给属性赋值的,所以set方法也是不可缺的,但是如果我们set方法没有形参,那么无论我们怎么给属性赋值,属性的值都是和对应字段名的值相同,只有形参存在时,我可以改变属性的赋值
②使用map:可以将数据中的字段名作为key,数据作为value存储在map对象中,resultType=“java.util.HashMap”
③resultType
如果实体类的属性名和字段名不相同的话可以采用map,也可以采用resultMap(和resultType不能共存),用户指顶属性名和字段名存储关系
在当前文件下定义标签
< resultMap id=“stu” type=“com.test.domain.Stu”>
< id column=“id” property=“myId” />
< result column=“name” property=“myName” />
< result column=“email” property=“myName” />
< result column=“age” property=“myAge”/>
</ resultMap>
关于实体类中的属性和字段名不等的情况,可以在sql语句中采用取别名的方法。但是耦合度高,不建议使用
五、动态sql
为了更加简便sql语句的书写,比如根据id查询和根据name查询,完全是条件的不同。
1、if标签
select * from student where 1 = 1
< if test="#{id} != null">
and id = #{id}
< /if>
< if test="@{#{name} != ‘’ || #{name} !=null}">
and name = #{name}
< /if>
缺点,我们需要自己完成字符串的拼接,而且不适合多个判断语句的书写,where后面必须加上恒等条件
2、where标签
< where>
< if test="#{name} != null && #{name} !='' ">
name = #{name}
< /if>
< if test="#{id} != null">
and id = #{id}
< /if>
< if test="#{address} != null">
and address = #{address}
< /if>
< /where>
解决if的存在的弊端,如果后面的if条件都不成立,那么没有where判断,如果存在多个条件的连接那么会自动的去掉第一个字符串前面的or/and,
3、foreach标签
主要确定in中的返回判断的,
select * from student where id in
< foreach collection=“array/list” item=“stu” open="(" close=")" separator=",">
stu
< /foreach>
标签中的属性分别表示 集合的类型, 遍历出来的元素的名称,字符串的开始符号, 字符串的结束符号,分隔符
后面还是多一个逗号,所以我们可以)的前面加一个-1,一个根本不存在的数据。
可以简写
select * from student where id in(
< foreach collection=“array/list” item=“stu”>
stu,
< /foreach> -1
)
4、set标签
用于在update中可以帮我们自动去除多个条件之间的,
eg:update student set name = ll,age = 20 where id = 2043242
如果通过if判断,不能够确定一个条件后面是否还有设置,这样肯定就会多出一个,和foreach一样的道理
六、一对多,一对一
1、一对多
如果从数据库中取出来的数据有一对多的关系,例如一个部门下面有多个员工,那么部门这个实体类中应该还存在一个存放员工信息的集合,这样我们就需要使用resultmap指定字段存入到实体类中属性中。
<resultMap id="" type="">
<id column="" property="" />
<result column="" property="" />
<collection>
<id column="" property="" />
<result column="" property="" />
</collection>
</resultMap>
2、一对一
一个实体类中的属性是另一个实体类的引用数据类型,一个具体的员工在一个部门当中
使用association标签
七、缓存
1、一级缓存
mybatis默认就会使用一级缓存,一级缓存是sqlsession级别的,也就是在一次会话中,对于查询的数据,会存入到缓存中,在sqlsession未关闭之前,也就是会话没有结束之前,再一次执行相同的sql时,不会从数据库中查询,而是从缓存中取出
缓存失效的情况:手动清空缓存,执行了增删改操作,查询语句不一致
2、二级缓存
原理:一次会话结束之后,缓存中的数据就会清空,对于第二会话中相同的查询,还是会继续从数据库中获取,所以二级缓存就是在一次会话结束后,sqlsession关闭的时候,将一级缓存中的数据存入到二级缓存中,每一个mapper都对应一个二级缓存,所以二级缓存是mapper级别的。
存在问题:如果mapperA中有涉及到mapperA和mapperB的查询,但是mapperB执行了增删改操作,由于每一个mapper对应一个二级缓存,在mapperA中,显然是没有修改过数据,所以查询的数据是mapperB未增删改之前的数据,而不是更新后的数据
实现:在mapper配置文件中加入cache标签,属性可以自己配置,如果readOnly设置为true,需要设置实体类可序列化(实现Serializable接口),取出来的对象是相等的。如果为false,返回的是一个新的对象,但是属性是相同的。
在主配置文件中加入setting设置,开启全局缓存。
3、缓存原理
执行查询操作时,首先会从二级缓存中获取,如果没有找到,那么从一级缓存中获取数据,也没有的话,才会执行sql语句连接数据库查询,然后将数据存储到一级缓存中,会话完成之后,更新二级缓存中的内容。
八、其他
1.主配置文件设置的补充
1.settings设置:
①打印日志
②设置数据库中的字段名以驼峰命名法映射到实体类中的属性名,
③设置实体类的别名
④开启全局缓存
2.主配置文件的数据库连接说明
此标签需要指定数据库的事务类型,可选JDBC/managed,表示采用JDBC中Connection的commit,rollback方式。可选Managed,把事务交给一个容器(服务器软件,框架)
< dataSource type="">
此标签表示使用连接池,type=“POOLED”,即使用连接池类PooledDataSource,创建的connection会存储在连接池中,以供下次使用,type=“UPOOLED”,表示不适用连接池,也会创建一个UnPooledDataSource,每次执行sql语句时都会创建一个新的connection对象。
3.数据库属性配置文件
可以将数据库的连接属性存储在properties文件中,在主配置文件中使用标签
< propertes resource="" />导入数据库信息配置文件,如果在类路径下,直接加上文件名就可以了
配置属性名和属性值
< property name=“属性名” value=“属性值”>
使用${属性名进行取用}
4.使用这里需要保证dao接口和xml文件同名,并且在同一个包下,
2.工具类
SqlSessionFactory是一个重量级的类,创建对象要耗费很多的时间,通常创建一次就可以,利用factory对象获取sqlsession对象进行数据库操作, sqlsession是非线程安全的,
public class MyBatisUtil {
public static SqlSessionFactory factory = null;//静态变量,编译阶段结汇执行.这个变量只需要声明一次就可以了,所以定义为静态变量,
static{//静态代码块,类加载时就会执行.也就是在编译阶段执行的.
try {
InputStream in = Resources.getResourceAsStream("mybatis.xml");
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = null;
if(factory!=null){
// sqlSession = getSqlSession(false);
sqlSession = factory.openSession();
}
return sqlSession;
}
public static SqlSession getSqlSession(boolean flag){
SqlSession sqlSession = null;
if(null!=factory){
sqlSession = factory.openSession(flag);
}
return sqlSession;
}
}
3.sql语句的复用
< sql id="">< /sql>,使用标签< include refid=“id” />可以进行字符串的拼接
4.注解式开发
可以直接接口方法上使用注解的形式声明执行的sql语句,对于简单的sql语句更为方便,但是sql语句过于复杂的话,建议使用映射文件。
@Select(),@Update(),@Delete(),@Insert()
连接子查询
@Results(
{@Result(column,property,One=@One(result="")),
@Result(column,property,Many=@Many(result="")
}
)
如果使用动态sql语句,如果使用包裹sql语句