Mybatis框架学习
学习介绍:
Mybatis是持久层框架:它针对的是jdbc的优化;使用它可以让数据库的操作变得简单;提高持久层数据处理的效率;
零:Mybatis简介
一:Mybatis基本使用:
对数据库单表的增删改查
二:Mybatis多表映射:
三:Mybatis动态语句
四:Mybatis高级扩展
五:总结
主要记忆api+配置即可
零:Mybatis简介:
Mybatis是持久层框架,可以简化数据库操作,但是需要我们自己写sql语句;
一:Mybatis快速入门:
步骤:
1.导入依赖
2.准备实体类(相关数据库信息)
3.创建一个mapper接口:(用来规定方法)
mapperxml文件:(用来写 方法对应的实现:专注写sql语句)
4.准备mybatis配置文件:
里面:链接数据库信息
mapper.xml文件的位置(最终api读的是mybatis配置文件)
.....
5.使用mybatis的api进行数据库查询即可
:SqlSessionFactory:会缓存配置的所有信息;哪个业务想要,就根据它创建一个SqlSession对象要获取mapper接口的代理对象,
之后:SqlSession对象要根据getMapper(接口.class)来获取mapper接口的代理对象,去找去找对应的sql语句
然后通过 "代理对象.方法"就可以进行数据库操作了;
二:Mybatis基本使用
(1)向SQL语句传参:
1.mybatis日志输出配置:
?
Mybatis日志配置:
<settings>
<!--!!!!!开启了Mybatis的日志输出,选择使用system进行日志进行控制台输出-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2.接收输入参数的细节:
方法外部传进来一个id值,要在这动态取到id值; 方式有两种:
a:“ #{key} ”:占位符+ 赋值 eg:第一步:emp_id=? 第二步:?=赋值
b:${key} :字符串拼接 eg:emp_id="" 然后将id与它拼接
推荐使用第一种,防止注入攻击;
注意:!占位符?,只能替代值的位置,不能替代 容器名(表名、列名,sql关键字)
使用第二种场景:一般用来拼接列名 表名;
eg:当我们需要:select * from 表 where 列名(列名是动态的) =动态的值
这时:不能使用第一种:因为占位符?,不能替代列名;
所以这时我们使用第二种;
总结:动态值:使用:#{};
动态列名、容器名、关键字:${}
(2)数据输入:
外部接口中的抽象方法传入的各种数据类型:如何在{}内定义key;
一:
(1)单个简单类型参数:eg:int / double / String等
eg:1.1
<!--
场景1:传入的是单个简单类型:在{}内部如何接值:
key随便写,但是一般写参数名;
-->
// 1.单个简单类型参数:
//根据id删除员工id信息:(DML语句:增删改查返回全是int)
int deleteById(Integer id);
//根据工资查询员工信息:
List<Employee> queryBySalary(Double salary);
如何在{}内接值:
<delete id="deleteById">
delete from t_emp where emp_id=#{ergz}
</delete>
(2)单个实体类型参数:{}内部的key如何写
//2.单个实体类型参数:
//插入员工数据:一般情况员工数据:是“ 实体对象 ”
//如何在{}内部取值
int insertEmp(Employee employee);
<!--场景二:传入的是实体类对象:key如何写:
key就=实体类的属性名即可;(实体类中定义的属性名)
-->
<insert id="insertEmp">
insert into t_emp (emp_name,emp_salary) value(#{empName},#{empSalary});
<!--1.因为是值:所以使用#{}; 2.-->
</insert>
</mapper>
(3)多个简单类型传入,key如何写:
//3.传入多个简单类型参数:
//根据员工姓名和工资查询员工信息:
//使用List:可能有重名员工
List<Employee> queryByNameAndSalary(@Param("a") String name, @Param("b") Double salary);
<!--场景3:
传入多个简单类型数据如何取值 :key如何写
方案1(常用):注解指定:@param注解:用来指定多个简单参数的key; ->:key=@Param("value值")
方案2:mybatis默认机制:
a:arg0 arg1...:形参从左到右依次对应 arg0 arg1...
(name,salary) name-> key=arg0 salary=arg1
b:param1 param2...
(name,salary) name->key=param1 salary-> key=param2
-->
<select id="queryByNameAndSalary" resultType="com.atguigu.pojo.Employee">
select emp_id empId ,emp_name empName,emp_salary empSalary
from t_emp where emp_name= #{a} and emp_salary=#{b}
<!--方案2:#{arg0} #{arg1}-->
</select>
二:复杂类型:多值类型 eg:实体类对象、map、list
(1)map类型:
//4.map:
//插入员工数据:但是员工数据装在map里面:map(name=员工的名字,salary=员工的薪水)
int insertEmpMap(Map data);
<!--场景4:传入map如何指定key的值
key=map的key即可;
-->
<insert id="insertEmplMap">
insert into t_emp (emp_name,emp_salary) value(#{name},#{salary});
</insert>
(3)数据输出:
数据输出主要有两种形式:
1.对应的标签中写的是DML语句(增删改):
不需要我们指定输出类型,默认返回值就是int,我们可以在外部可以直接使用int/long接值;
2.SQL语句查询:(主要)
我们需要在 标签的 "resultType"指定输出对应的类型;
!!!即在进行查询时:根据我们数据输出/返回 的 数据类型:应如何指定resultType
1.单个简单类型
//(1)如果我们deml语句:插入/修改/删除:
/*
返回值:是int/long
*/
int deleteById(Integer id);
<delete id="deleteById">
delete from t_emp where emp_id=#{id}
</delete>
//(2)需要我们指定输出类型的:一般为查询语句:
//需求:根据员工的id查询员工的姓名:
String queryNameById(Integer id);
//根据员工的id查询员工的工资
Double querySalaryById(Integer id);
<!--
输出场景1:查询的是:单个简单类型,返回单个简单类型,如何指定resultType:(即如何指定返回值的类型);
resultType语法:
1.类的全限定符合:java.lang.String 、java.lang.Double
2.别名简称:string、double;
Mybatis给我们提供了72种默认的别名!:这些都是我们常用的Java数据类型;
eg:如果是基本数据类型是:int、double:->:_int _double
如果是包装数据类型:Integer、Double:->小写即可:int/integer double
如果是集合/容器类型:Map、List、HashMap:->小写即可...
如果没有提供的需要我们自己定义或者 写 类的全限定符号
扩展:自定义的类如何定义别名:在mapper.xml:添加标签 <typeAliases>
<typeAliases>
a:单独定义:有一个自定义类:就在配置文件中:“ 单独 ”定义一个:<typeAliases>标签
<typeAlias type="com.atguigu.pojo.Employee" alias="ergouzi"></typeAlias>
b:批量定义:会将这个pojo包下:批量将包下的所有的类都给与别名:别名就是类名的首字母小写
之后我们在写的时候:别名可以直接写:类名的首字母小写;
<package name="com.atguigu.pojo"/>
</typeAliases>
-->
<select id="queryNameById" resultType="java.lang.String">
select emp_name from t_emp where emp_id=#{id}
</select>
<select id="querySalaryById" resultType="java.lang.Double">
select emp_name from t_emp where emp_id=#{id}
</select>
总结:DML不需要指定返回类型,只有查询语句需要指定类型:
第一种:类的全限定符
第二种:别名;
2.返回实体类对象
//(3.)返回单个自定义实体类;:根据id查询员工信息:
Employee queryById(Integer id);
场景二:返回单个自定义类型:
返回自定义类型:可以写类的全限定符、也可以写别名;
注意:a:为什么我们在查询的时候 需要起别名:
如果我们是查询 返回单个实体类型,要求:列名和实体类的属性名要一致!
这样才可以进行实体类的属性的映射
b:因为数据库是:蛇形命名,而java是驼峰命名
但是可以进行设置,设置支持驼峰自动映射;
数据库:emp_id java:empId:
只要符合数据库的蛇形命名,java的驼峰命名,这样就可以自动映射了;
-->
<select id="queryById" resultType="com.atguigu.pojo.Employee">
3.返回map类型
(4)返回map类型:
场景:如果想要返回多个值:eg:员工id,工资,平均工资;但没有实体类,我们可以使用map接值
注意:列名就是对应的key,
最终查询到的结果就会放到map的值中;
/*
查询部门的最高工资和平均工资
*/
接口:
Map<String,Object>selectEmpAndMaxSalary();
mapper.xml文件中:
<!--场景三:
返回map类型:
查询的时候没有实体类,但我们还想要保留key:可以使用map接收数据;
key->就是查询的列;
value-> 查询的值
-->
<select id="selectEmpAndMaxSalary" resultType="map">
SELECT
emp_name 员工姓名,
emp_salary 员工工资,
(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
FROM t_emp WHERE emp_salary=(
SELECT MAX(emp_salary) FROM t_emp
)
</select>
4.返回集合类型:
(5)返回集合类型如何指定:
a: 接口:
//(5)返回集合类型:
//查询工资高于传入值的 员工姓名:分析:一个员工姓名是:String; 多个是:List<String>
List<String> queryNameBySalary();
//查询数据库中全部员工信息; 分析:一个员工信息:为Employee对象,多个为List<Object>
List< Employee> queryAll();
b:mapper.xml文件:
<!--
场景四:返回集合类型
注意:返回值是集合,:resultType:指定的是:泛型!!!
-->
<!--a:-->
<select id="queryNameBySalary" resultType="string">
select emp_name from t_emp where salary> #{salary}
</select>
<!--b:-->
<select id="queryAll" resultType="employee">
select * from t_emp;
</select>
5.如何使用mybatis插入并返回自增长的主键
a:接口:
//(6)员工插入:
int insertEmp(Employee employee);
b:mapper.xml文件
<!--场景5:
主键回显:获取插入数据的 主键
1.自增长主键回显:主键是由数据库维护的:auto_increment
eg:如何在插入的时候:获取自增长主键:即->主键回显
在mapper.xml文件中:在insert标签中:添加
useGeneratedKeys="true":代表:我们想“ 要 ”数据库自动增长的主键值;
keyColumn="empId":是主键列的值;(即数据库中哪一列是增长的)
keyProperty="empId":接收主键列值的属性!!!(java实体类中的属性;即把主键列值赋给java实体类的属性)
最终可以在插入的实体类的属性上。取到自增长的值;
2.
-->
<insert id="insertEmp" useGeneratedKeys="true" keyColumn="empId" keyProperty="empId">
insert into t_emp (emp_name,emp_salary)
value (#{emp_name} #{emp_salary});
</insert>
c:测试类:
public void test_01() throws IOException {
//1.读取外部配置文件:mybatis-config:
InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
//3.获取SqlSession对象
//!!!这句话:opemSession:默认自动开启事务,不会提交,最后需要我们自己提交sqlSession.commit();
SqlSession sqlSession = sqlSessionFactory.openSession();
/*
方法2:!!!!!!!!!!!!!!!!!!
或者我们使用:openSession(true):自动开启事务,也会自动提交事务,就不需要最后commit了;
*/
//4.获取代理mapper对象
//mapper对象能够根据方法和类的信息帮我们调用对应的增删改查方法
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);//传入接口
//使用mapper对象进行增删改查!!!!!!!!!!!!!!!
Employee employee=new Employee();
employee.setEmpSalary(888.0);
employee.setEmpName("二狗子");
System.out.println(employee.getEmpId());//插入之前:输出主键
System.out.println("------------------");
int rows=mapper.insertEmp(employee);
System.out.println(employee.getEmpId());//插入之后:输出主键
//最终可以在插入的实体类的属性:EmpId方法上。取到自增长的值:因为将主键增长列赋给了java的empId属性!!!!!!!!!!
System.out.println("rows"+rows);
//5.释放资源和提交事务:
//必须提交事务:因为:我们执行了非查询语句,所以最好必须提交事务;要不然不会更新数据库;
sqlSession.commit(); //上面执行的是DML语句时:必须要手动提交,因为是自动开启事务,必须要提交!!!
sqlSession.close();
}
6.非自增长类型主键维护:如何交给mybatis给我们维护
eg:
主键:t_id varchar(64) primary key :是非自增长的主键,在插入数据时,我们每次都要自己插入id,那么我们如何将插入id的步骤交给mybatis呢:
1Teacher接口
public interface TeacherMapper {
//插入数据,返回的是int:
int insertTeacher(Teacher teacher);
}
2:在实现类中书写方法实现-->
<mapper namespace="com.atguigu.mapper.TeacherMapper">
<insert id="insertTeacher">
<!--注意:因为是非自增长类型:t_id varchar(64) primary key
但是:主键维护太麻烦了,我们可以将非自增长主键交给Mybatis来维护,即我们不想自己赋值,让mybatis自己生成数字, 然后赋给id!!!
实现方法: !!!在<select>标签内部使用: <selectKey>标签!!!!
order="BEFORE":指: a:sql语句是在插入语句之前执行;(还是之后)
resultType="string": b:UUID查询返回的类型:字符串:string(别名):
keyProperty="": c:查询的结果:给哪个属性赋值:tId(Teacher的属性名);
-->
<selectKey order="BEFORE" resultType="string" keyProperty="">
select Replace(UUID(),'-',''); <!--uuid:自动生成一个不重复的id-->
</selectKey>
这条uuid语句的执行过程:先使用这条sql语句:UUID生成不重复的数字,然后再赋给Teacher的属性tId
然后我们直接拿着这个属性tId执行insert语句即可!!!!!!!!!
//insert语句:
insert into Teacher (t_id,t_name) value (#{t_id},#{t_name});
<!--因为主键不是自增长:在赋值的时候需要给id也赋值!!!-->
</insert>
</mapper>
3.测试类:
@Test
public void test_02() throws IOException {
//1.读取外部配置文件:mybatis-config:
InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
//3.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//true:代表自动提交:最后就不用手动commit了!!!!!!
//4.获取代理mapper对象
//mapper对象能够根据方法和类的信息帮我们调用对应的增删改查方法
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher=new Teacher();//teacher对象
teacher.settName("hhh");
//自己维护主键:
/*String id= UUID.randomUUID().toString().replaceAll("-","");teacher.settId(id);*/
//我们自己在xml文件中让mybatis维护id!!!!!
System.out.println(teacher.gettId()); //在执行插入之前:输出Teacher的Id:为“null”;
//使用获取到的mapper代理对象:mapper进行增删改查操作
int i=mapper.insertTeacher(teacher); //插入语句!!!!!!!
System.out.println(teacher.gettId()); //在执行插入之后:输出Teacher的Id:有了值;
System.out.println("i"+i);
//5.关闭资源/提交事务(事务是自动开启的,但不是自动提交)
sqlSession.close();
}
两种主键回显小结:
总结:如果是:
1.自增长主键类型:想要回显:
<insert id="insertEmp" useGeneratedKeys="true" keyColumn="empId" keyProperty="empId">
想要回显 数据库列名 实体类属性名
最后:他就会把自增长的主键列名,赋给属性;
如果是:
2.非自增长的主键:想要回显:交给mybatis去维护
<select>
<selectKey order="BEFORE" resultType="string" keyProperty="">
select Replace(UUID(),'-',''); <!--uuid:自动生成一个不重复的id-->
</selectKey>
</select>