前置概念
三层架构:
界面层:和用户打交道,提供应用程序的访问接口(jsp,html,servlet,springMVC)
业务逻辑层:服务层,负责逻辑处理,调用DAO层 (XXXService类,spring)
数据访问层:与数据库进行交互 (XXXDao层,mybatis)
MVC三层结构
Model view control三层结构是三层架构中的界面层的具体细化
什么是mybatis
mybatis是 MyBatis SQL Mapper Framework for Java,封装了JDBC的功能
开发人员提供sql语句–mybatis处理sql—开发人员得到List集合或java对象(表中的数据)
什么是ORM
ORM(object relational mapping)对象关系映射
java语言对象与数据库的关系之间的数据互换称为一种映射,整套操作就是ORM
使用步骤
基础使用流程
1. maven中加入mybatis依赖
pom文件中加入
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
2. 创建Dao接口,定义操作数据库的方法
//操作Student表
public interface StudentDao {
public List<Student> selectStudents();
}
3. 创建mapper文件,也叫作sql映射文件:写sql语句,和接口中方法对应的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">
<!--
namespace:必须有值,自定义的唯一字符串
推荐使用:dao 接口的全限定名称
-->
<mapper namespace="com.learning.dao.StudentDao">
<!--
<select>: 查询数据, 标签中必须是 select 语句
id: sql 语句的自定义名称,推荐使用 dao 接口中方法名称,
使用名称表示要执行的 sql 语句
resultType: 查询语句的返回结果数据类型,使用全限定类名
-->
<select id="selectStudents" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
select no,name,cno from t_student order by no
</select>
</mapper>
4. 创建mybatis的主配置文件:1)连接数据库 2)知道mapper文件的位置
<?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>
<!--配置 mybatis 环境-->
<!--输出日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--environments里可以包含多个environment,通过default指定-->
<environments default="mysql">
<!--id:数据源的名称,本environment的唯一标识-->
<environment id="mysql">
<!--配置事务类型:
"JDBC":使用 JDBC 事务(使用 Connection 的提交和回滚)
"MANAGED": 把mybatis的事务处理委托给其他容器(一个服务器软件,一个框架(spring))
-->
<transactionManager type="JDBC"/>
<!--数据源 dataSource:创建数据库 Connection 对象
type: POOLED 使用数据库的连接池
-->
<dataSource type="POOLED">
<!--连接数据库的四个要素-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sqltest"/>
<property name="username" value="root"/>
<property name="password" value="caocao4352"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--方式1-->
<mapper resource="com/learning/dao/StudentDao.xml"/>-
<!--方式2-->
<package name="com.learning.dao"/>
</mappers>
</configuration>
<!--
mybatis主配置文件:定义数据库的配置信息,sql映射文件的位置
-->
5. 使用mybatis对象SqlSession,通过他的方法执行sql语句
//visit mybatis and read Student data
String config="mybatis.xml";
//read config file
InputStream in=Resources.getResourceAsStream(config);
//build SqlSessionFactoryBuilder for gain SqlSession
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(in);
//obtain SqlSession
SqlSession sqlSession=factory.openSession();
//execute SqlSession
String sqlId="com.learning.dao.StudentDao"+"."+"selectStudents";
List<Student> studentList=sqlSession.selectList(sqlId);
//put result
studentList.forEach(student -> System.out.println(student));
//close sqlSession
sqlSession.close();
主函数中读取mybatis.xml文件->通过mybatis.xml文件连接数据库,获取StudentDao.xml文件的位置->主函数中的sqlId指定StudentDao.xml文件中具体要执行哪个sql语句
添加新的SQL需求
mybatis封装了jdbc的通用步骤,并将sql语句分离到xml配置文件中,解耦合性很高。当再写一个新的sql语句需求时,只需要
- 在接口类中创建新的方法
//操作Student表
public interface StudentDao {
public List<Student> selectStudents();
//插入方法 新的!!!!!!!!
public int insertStudent(Student student);
}
- 在该接口类对应的xml文件中加入sql语句
<!--插入操作-->
<insert id="insertStudent">
insert into t_student values(#{no},#{name},#{cno})
</insert>
insert语句需要填入占位符,满足student数据动态填入
- 保持上述一样的配置语句,在下方新加
//execute SqlSession
String sqlId="com.learning.dao.StudentDao"+"."+"insertStudent";
Student student=new Student();
student.setName("harden");
student.setNo(51);
student.setCno(101);
int nums=sqlSession.insert(sqlId,student);
//手动提交事务
sqlSession.commit();
insert语句返回int表示操作的数据库行数,并且需要手动提交事务
更新操作
//基础写法
<update id="updateStudent">
update student set age = #{age} where id=#{id}
</update>
//确保无空值引入
<update id="updateStudent">
update t_student
<set>
<if test="no >0">
no= #{no},
</if>
<if test="name != null and name != '' ">
name= #{name},
</if>
<if test="cno != null and cno != '' ">
cno= #{cno},
</if>
</set>
where no=#{no}
</update>
利用工具类抽象声明和动态代理简化步骤
工具类抽象
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory=null;
static {
String config="mybatis.xml";
try {
InputStream in= Resources.getResourceAsStream(config);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(in); //此步骤开销大,所以用static避免重复开辟
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession的方法
public static SqlSession getSqlSession() {
SqlSession sqlSession=null;
if(sqlSessionFactory !=null){
sqlSession= sqlSessionFactory.openSession(); //非自动提交事务
}
return sqlSession;
}
}
动态代理简化流程
public static void main(String[] args) {
//通过工具类简化sqlSession的步骤
SqlSession sqlSession= MyBatisUtils.getSqlSession();
//利用反射机制直接填写需要调用的dao层类
StudentDao dao= sqlSession.getMapper(StudentDao.class);
List<Student> res=dao.selectStudents();
for(Student stu:res){
System.out.println("学生= "+stu);
}
}
传参方法
简单类型传参
一个简单类型的参数:
简单类型: mybatis把java的基本数据类型和String都叫简单类型。
在mapper文件获取简单类型的一个参数的值,使用 #{任意字符}
接口:public Student selectStudentById(Integer id)
mapper:select id,name, email,age from student where id=#{studentId}
多个参数传参
使用@Param 命名参数:
接口:
public List<Student> selectMultiStudents(@Param("myNo")Integer no,
@Param("myName")String name);
StudentDao.xml:
<select id="selectMultiStudents" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
select no,name,cno from t_student where no=#{myNo} or name=#{myName}
</select>
调用:
SqlSession sqlSession= MyBatisUtils.getSqlSession();
StudentDao dao= sqlSession.getMapper(StudentDao.class);
List<Student> res=dao.selectMultiStudents(1,"rose");
结果
使用java对象作为参数:
接口:
public List<Student> selectMultiStudents2(Student stu);
StudentDao.xml:
<select id="selectMultiStudents2" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
这里{参数}:参数是类的成员变量名
select no,name,cno from t_student where no=#{no} or name=#{name}
</select>
public class Student {
private int no;
private String name;
private int cno;
}
调用:
SqlSession sqlSession= MyBatisUtils.getSqlSession();
StudentDao dao= sqlSession.getMapper(StudentDao.class);
Student student=new Student(1,"jack",100);
List<Student> res=dao.selectMultiStudents2(student);
结果
使用map作为参数:
接口:
key作为查询键,value作为想匹配的内容
public List<Student> selectMultiStudents3(Map<String,Object> map);
StudentDao.xml:
<select id="selectMultiStudents3" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
select no,name,cno from t_student where no=#{myNo} or name=#{myName}
</select>
调用:
SqlSession sqlSession= MyBatisUtils.getSqlSession();
Map<String,Object> map=new HashMap<>();
map.put("myNo","3");
map.put("myName","rose");
List<Student> res=dao.selectMultiStudents3(map);
结果
占位符 # Or $
#{参数}:在数据库里用 ?代替,表示列值
${参数}: 在数据库里直接替换字符串,可能存在SQL注入的问题
返回结果
除了类型的全限定名称作为返回结果
<select id="selectLikeOne" resultType="com.learning.domain.Student">
还可通过别名和包名:
定义别名
xml文件中,设置返回的结果resultType使用全限定名称"com.learning.domain.Student"
<select id="selectMultiStudents3" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
select no,name,cno from t_student where no=#{myNo} or name=#{myName}
</select>
resultType可以设计别名:
方式1:
1)在mybatis主配置文件中定义,使<typeAlias>定义别名
<typeAlias type="com.learning.domain.Student" alias="stu"/>
然后在xml文件里使用
<select id="selectMultiStudents3" resultType="stu">
2)在mybatis主配置文件中定义包名,然后可以直接在resultType里使用类名
<package name="com.learning.domain"/>
然后在xml文件里使用
<select id="selectMultiStudents3" resultType="Student">
ResultMap解决名和类成员名不一致或存在一对多多对一关系
- 数据库列名核类成员名的名称不一致
resultMap提供一种数据库列与类成员变量的对应关系,在前面的resultType的方式中,要保证类成员变量名与数据库列名保持一致,当不一致时会匹配不到。resultMap通过映射的方式解决了这一问题。
<resultMap id="StudentMap" type="com.learning.domain.Student">
//column 数据库列名 property 类成员名
//id表示主键 result表示普通列
<id column="no" property="stuNo"/>
<result column="name" property="stuName"/>
<result column="cno" property="stuCno"/>
</resultMap>
//resultType->resutlMap
<select id="selectStudentMap" resultMap="StudentMap">
<!--要执行的 sql 语句-->
select no,name,cno from t_student
</select>
- 多表关联,存在一对多或多对一关系 (下文介绍)
LIke模糊匹配
#{name} 中的name设置为字符串 ”%xxx%“
<select id="selectLikeOne" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
select no,name,cno from t_student where name like #{name}
</select>
动态SQL
动态sql: sql的内容是变化的,可以根据条件获取到不同的sql语句。
主要是where部分发生变化。
动态sql的实现,使用的是mybatis提供的标签, <if> ,<where>,<foreach>
if
1)<if>
是判断条件的,
语法
<if test="判断java对象的属性值">
部分sql语句
</if>
示例:当name不为空或no大于0时,执行sql语句
<select id="selectIfTest" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
select no,name,cno from t_student where
<if test="name != null and name !=''">
name = #{name}
</if>
<if test="no > 0">
or no > #{no}
</if>
</select>
where
2)<where>
用来包含 多个<if>
的, 当多个if有一个成立的, <where>
会自动增加一个where关键字,并去掉 if中多余的 and ,or等。
<select id="selectIfTest" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
select no,name,cno from t_student
<where>
<if test="name != null and name !=''">
name = #{name}
</if>
<if test="no > 0">
or no > #{no}
</if>
</where>
</select>
例如,当前面的name为空时,where会自动去掉no前面的or,
foreach
3)<foreach>
循环java中的数组,list集合的。 主要用在sql的in语句中。
<foreach collection="" item="" open="" close="" separator="">
collection:表示接口中的方法参数的类型, 如果是数组使用array , 如果是list集合使用list
item:自定义的,表示数组和集合成员的变量
open:循环开始是的字符
close:循环结束时的字符
separator:集合成员之间的分隔符
<select id="selectForTest" resultType="com.learning.domain.Student">
<!--要执行的 sql 语句-->
select no,name,cno from t_student
where name in
<foreach collection="list" item="myname" open="(" close=")" separator=",">
#{myname}
</foreach>
</select>
生成语句
select no,name,cno from t_student where name in ( ? , ? , ? )
代码片段
sql代码片段, 就是复用一些语法
步骤
1.先定义 <sql id="自定义名称唯一"> sql语句, 表名,字段等 </sql>
2.再使用, <include refid="id的值" />
使用配置文件配置数据库参数
同jdbc的properties一样,mybatis也使用properties文件作为数据库参数,将连接数据库的步骤从mybatis.xml里抽出来。
首先在resources文件夹里创建properties文件
然后添加数据库信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sqltest
jdbc.user=root
jdbc.password=caocao4352
在mybatis.xml里的更改
在顶部
<!--指定properties-->
<properties resource="jdbc.properties"/>
然后将datasource部分更改为
<dataSource type="POOLED">
<!--连接数据库的四个要素-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
通过"${jdbc.XXX}"代替原有字符串
关联关系表
一对多关系 xml mapper里用collection关联
多对一关系 xml mapper里用association关联
一对多关系
当出现如下关系:一个用户有多个订单,一对多的映射关系存在时,mybatis可以提供一种类对象
Customer{
id=1, name='张三', age=22,
ordersList=[
Orders{id=11, orderNumber=20, orderPrice=22.22},
Orders{id=12, orderNumber=60, orderPrice=16.66}]
}
id name这些重复的元素只存一次,每个订单信息通过一个list集合存储。下面是类的定义方式:
//一对多的一:
public class Customer {
private Integer id;
private String name;
private Integer age;
private List<Orders> ordersList;
}
//一对多的多
public class Orders {
private Integer id;
private String orderNumber;
private Double orderPrice;
}
//接口定义
public interface CustomersDao {
Customer getById(Integer id);
}
xml文件中的核心在于通过resultMap定义映射关系,并且多表联查时一定要起别名,如果直接使用c.id在mybatis中无法对照。
resultMap中有collection模块,用于设置类成员中的list集合的对照关系
<mapper namespace="com.learning.dao.CustomersDao">
<resultMap id="customersMap" type="com.learning.domain.Customer">
<id column="cid" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<!--通过colletion集合方式-->
<collection property="ordersList" ofType="com.learning.domain.Orders">
<id column="oid" property="id"/>
<result column="orderNumber" property="orderNumber"/>
<result column="orderPrice" property="orderPrice"/>
</collection>
</resultMap>
<select id="getById" parameterType="int" resultMap="customersMap" >
<!--要执行的 sql 语句-->
select c.id cid,name,age,o.id oid,orderNumber,orderPrice,customer_id
from customer c
left join orders o
on c.id=o.customer_id
where c.id=#{id}
</select>
</mapper>
多对一关系
以orders订单类为中心看与customers的关系,就是多对一的关系。
要满足以订单id查找下该单的用户,xml文件里用association关联
Orders{
id=11, orderNumber='20', orderPrice=22.22,
customer=Customer{id=1, name='张三', age=22, ordersList=null}}
<mapper namespace="com.learning.dao.OrdersDao">
<resultMap id="OrdersMap" type="com.learning.domain.Orders">
<id column="oid" property="id"/>
<result column="orderNumber" property="orderNumber"/>
<result column="orderPrice" property="orderPrice"/>
<association property="customer" javaType="com.learning.domain.Customer">
<id column="cid" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</association>
</resultMap>
<select id="getById" parameterType="int" resultMap="OrdersMap" >
<!--要执行的 sql 语句-->
select o.id oid,orderNumber,orderPrice,c.id cid,name,age
from customer c
left join orders o
on c.id=o.customer_id
where o.id=#{id}
</select>
</mapper>
MyBatis逆向工程
由官方提供的一个工程模板,可以根据指定的数据库自动生成针对单表的所有的常规的增删改查的配置及接口。可以极大的方便设计者,不用再去创建大量的配置及书写接口,从而把精力集中到业务逻辑的实现上去,它的源码存放在github上,也提供了相关的具有逆向工程功能的Maven插件。
使用时,
- 需要先在
pom.xml
文件里加入插件
<build>
<plugins>
<!--myBatis逆向工程插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
- 然后在resource里添加用于连接数据库的properties文件和配置文件
generator.properties:
jdbc.driverLocation=D:/files/apache-maven-3.8.5/repository/mysql/mysql-connector-java/8.0.27/mysql-connector-java-8.0.27.jar
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://127.0.0.1:3306/crm
jdbc.userId=root
jdbc.password=xxxxx
generatorConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--指定mysql数据库驱动-->
<!--<classPathEntry location="E://repository-p2p//mysql//mysql-connector-java//5.1.43//mysql-connector-java-5.1.43.jar"/>-->
<!--导入属性配置-->
<properties resource="generator.properties"></properties>
<!--指定特定数据库的jdbc驱动jar包的位置-->
<classPathEntry location="${jdbc.driverLocation}"/>
<context id="default" targetRuntime="MyBatis3">
<!-- optional,旨在创建class时,对注释进行控制,false生成注释,true无注释 -->
<commentGenerator>
<property name="suppressDate" value="false"/>
<property name="suppressAllComments" value="false"/>
</commentGenerator>
<!--jdbc的数据库连接 -->
<jdbcConnection
driverClass="${jdbc.driverClass}"
connectionURL="${jdbc.connectionURL}"
userId="${jdbc.userId}"
password="${jdbc.password}">
</jdbcConnection>
<!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制-->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- Model模型生成器,用来生成含有主键key的类,记录类 以及查询Example类
targetPackage 指定生成的model生成所在的包名
targetProject 指定在该项目下所在的路径|指定生成到的工程名称
-->
<javaModelGenerator targetPackage="com.bjpowernode.crm.settings.domain"
targetProject="E:/code/crm_project/crm/src/main/java">
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="false"/>
<!-- 是否对model添加 构造函数 true添加,false不添加-->
<property name="constructorBased" value="false"/>
<!-- 是否对类CHAR类型的列的数据进行trim操作 -->
<property name="trimStrings" value="true"/>
<!-- 建立的Model对象是否 不可改变 即生成的Model对象不会有 setter方法,只有构造方法 -->
<property name="immutable" value="false"/>
</javaModelGenerator>
<!--Mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 -->
<sqlMapGenerator targetPackage="com.bjpowernode.crm.settings.mapper"
targetProject="E:/code/crm_project/crm/src/main/java">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码
type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象
type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口
-->
<javaClientGenerator targetPackage="com.bjpowernode.crm.settings.mapper"
targetProject="E:/code/crm_project/crm/src/main/java" type="XMLMAPPER">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<table tableName="tbl_user" domainObjectName="User"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
其中,利用<table /table>
标签指定生成的具体表,
3. 使用maven插件生成逆向工程类
4. 生成效果: