MyBatis
##1.什么是mybatis
mybatis是apache的一个开源项目,他是一个优秀的持久层框架,他对jdbc的操作数据库的过程及逆行封装,使开发者只需关注SQL本身,而不需要去处理注册驱动、创建connection、创建statement、手动设置参数、结果集检索等。
MyBatis通过xml或注解的方式将要执行的各种statement(传输入)(statement、preparedStatement)配置起来,通过java对象和statement中的sql进行映射生成最终的sql语句,最后由mybatis框架执行sql并将结果映射成java对象返回。
总之,就是对jdbc访问数据库的过程进行了封装,简化了jdbc代码,解决了jdbc将结果封装为java对象的麻烦。
###回顾JDBC进行查询,并返回结果集
- 利用反射来注册驱动
Class.forName("com.mysql.jdbc.Driver");
- 获取连接对象三个参数,url(库路径),username(用户名),password(密码)
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/EMP?characterEncoding=utf-8","root","123456");
- 获取传输器
stat=conn.createStatement();
- 执行sql语句,并返回执行的结果
String sql="select * from emp";
rs=stat.executeQuery(sql);
- 处理结果集
> //创建list集合
List<Emplist=new ArrayList<Emp>(); //用rs.next() 来判断是否有下一行 while(rs.next()) { //将每一条记录封装成集合 Emp emp=new Emp(); //要把表记录中第一行取出来设置为emp中的id属性 emp.setId(rs.getInt("id")); emp.setName(rs.getString("name")); emp.setJob(rs.getString("job")); emp.setSalary(rs.getDouble("salary")); list.add(emp); } return list;
6.释放资源
//释放资源的方法
public static void cloose(Connection conn,Statement stat,ResultSet rs) {
/*
* 判断三个对象是否为空,如果不为空,则进行关闭,并设置为空
*/
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}finally {
rs=null;
}
}
if(stat!=null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}finally {
stat=null;
}
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}finally {
conn=null;
}
}
}
}
#####当然我们可以将程序放入servlet中,在本地服务器上运行,在WEB-INF下添加emp.jsp,用表格的方式显示出来。在EmpServlet类中写
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//接收请求方式。接收字节流
response.setContentType(“text/html;charset=utf-8”);
PrintWriter out = response.getWriter();
//通过out的方法向外写数据
//调用TestJdbc的findAll方法,查询所有员工的信息
List<Emplist=TestJdbc.findAll();
//将员工集合存入request域中
request.setAttribute(“list”, list);
//通过转发,将员工集合带到index.jsp遍历取出显示
request.getRequestDispatcher(“index.jsp”).forward(request, response);
}
emp.jsp,也可以设置表格属性
<!-- 想从域中取数据,并遍历 需要jstl -->
<h3>遍历员工集合</h3>
<table>
<c:forEach items="${list }" var="emp">
<tr>
<td>${emp.id}</td>
<td>${emp.name}</td>
<td>${emp.job}</td>
<td>${emp.salary}</td>
</tr>
</c:forEach>
</table>
访问EmpServlet,启动tomcat,打印员工信息表
JDBC的缺点,为啥要mybatis
-
用JDBC对数据库操作效率低,重复代码太多,(注册驱动,获取连接等),但是mybatis可以简化JDBC代码。
-
JDBC自身并没有什么连接池,会频繁的创建和连接和关闭,导致程序效率更低了。mybatis自身支持连接池,可以提高效率。
-
JDBC的SQL语句都是写死的,每一次改动,都会重新编译。mybatsi是将SQL配置在xml文件中,修改sql只需要修改配置文件,不需要重新再编译。
-
JDBC执行h后返回的结果集合需要我们手动处理,不叫繁琐。mybatis对查询SQL执行后返回的的ResultSet对象,会帮我们自动处理转为Java对象。
##写一个简单MyBatis项目 -
创建maven工程
-
配置pom文件(导入:mysql驱动包,mybatis整合包,junit单元测试包,slf4j-log4j12日志包)
-
创建一个测试类TestMybatis,提供一个findAll方法,写一个测试单元方法。
-
在resources下配置两个xml文件,mybatis-config.xml(mybatis的核心配置文件)和EmpMapper.xml(映射),所有的关于Emp表的sql语句都在映射中。
-
配置mybatis-config.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"> <!-- MyBatis的全局配置文件 --> <configuration > </configuration>
-
配置EmpMapper.xml文件
<?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一般指定为当前文件的所在包路径+文件名 将来在程序中通过[ namespace + id ]定位到执行哪一条SQL语句 --> <mapper namespace="com.tedu.pojo.EmpMapper"> <!-- 通过select、insert、update、delete标签声明要执行的SQL --> <select id="findAll" resultType="com.tedu.pojo.Emp"> select * from emp </select> <!-- resultType:返回值类型,简单类型(例如:Integer,String,Emp等) 如果返回集合(List<Emp>),只需配置集合中的元素类型即可! resultMap:复杂对象结构(例如多表关联查询等),后面用到再讲解 --> </mapper>
-
在mybatis-config.xml中引入EmpMapper.xml 文件,按住shift是否能跳转到EmpMapper.xml中
<!-- 2.导入mapper.xml文件(其中包含了我们要执行的sql语句) --> <mappers> <!-- resource指为源码的目录 --> <mapper resource="EmpMapper.xml"/> </mappers>
-
配置mybatis-config.xml文件
##配置开发环境
org.apache.ibatis.exceptions.PersistenceException:
Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for EmpMapper.findAll
Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for EmpMapper.findAll
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:26)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:111)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98)
at com.yjq.mybatis.TestMybatis.findAll(TestMybatis.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Caused by: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for EmpMapper.findAll
at org.apache.ibatis.session.Configuration$StrictMap.get(Configuration.java:797)
at org.apache.ibatis.session.Configuration.getMappedStatement(Configuration.java:631)
at org.apache.ibatis.session.Configuration.getMappedStatement(Configuration.java:624)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:107)
... 26 more
这个错误的原因集合里面不包含id或者值,有问题,检查你的namespce或者id的是否是一致的
###2.mybaitsi执行的过程
-
配置好Mapper.xml文件和mybatis-config.xml文件
-
创建普通java类对象,并提供set和get方法
//生成对应的get和set方法 private Integer id; private String name; private String job; private Double salary; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name;
-
创建TestMybatis类,进行增删改查的测试
public void findAll() throws IOException { //1.读取mybatis的核心配置文件(mybatis-config) InputStream in=Resources.getResourceAsStream("mybatis-config.xml"); //2.通过配置获取一个SqlSessionFactory,sqlsession工厂,他是用来生成SqlSession对象的 SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in); //3.通过工厂获取袷SqlSession对象,类似于JDBC的Connection,它可以获取对象,处理,并返回结果 SqlSession session = factory.openSession(); //4.执行SQL语句(namespace+id) List<Emp> selectList = session.selectList("EmpMapper.findAll"); //返回一个List<Emp> empList=... //5.输出结果 for (Emp emp : selectList) { System.out.println(emp); }
-
在EmpMapper.xml中写我们的Sql语句
<mapper namespace="EmpMapper">
<!-- 通过select、insert、update、delete标签声明要执行的SQL -->
<select id="findAll" resultType="com.yjq.pojo.Emp">
select * from emp;
</select>
<!--
resultType:返回值类型,简单类型(例如:Integer,String,Emp等)
如果返回集合(List<Emp>),只需配置集合中的元素类型即可!
resultMap:复杂对象结构(例如多表关联查询等),后面用到再讲解
-->
<!-- 一般我们不会在增删改上指定 resultType,因为他们返回的是int类型的,
(也就是影响的结果行数),但是每个sql语句的id值不能相同,指定的类型就是该表的类型-->
<insert id="insert">
insert into emp values(null,'张三','高级java开发工程师',35000);
</insert>
###这里一定要将数据库连接,并保证有正确的库
##注意:
1.namespace一般指定为当前文件的所在包路径加文件名,程序通过namespcae+id可以定位到指定的Sql语句。
2.EmpMapper.xml中sql语句的id必须和TestMybatis类中的执行sql语句中的id一致
selectList("EmpMapper.findAll");
<select id="findAll" resultType="com.yjq.pojo.Emp">
###3.为了解决我们不把sql语句写死,换的灵活点,引用了占位符,
JDBC的占位符为?
MyBatis的占位符为#{属性名},属性名就是表中的属性列名
-
如果我们需要传入的值比较多,但是执行sql语句的只能有两个参数
int row = session.insert("EmpMapper.insert"," ");
-
那如何解决多个传入的参数,我们可以将参数进行封装,然后再传入,把封装后的对象再传入,可以用该表类型封装,也可以用map集合。
######先用Emp表进行封装public void testUpdate() {
update emp set name=#{name},job=#{job},salary=#{salary} where id=#{id};
//创建Emp对象,对参数进行封装
Emp e=new Emp();
e.setId(5);
e.setName(“杭州马”);
e.setJob(“AliabaaCEO”);
e.setSalary(3431000000000d);
int row = session.update(“EmpMapper.updateById”,e);
//提交事务,不然会回滚
session.commit();
System.out.println(row);
}
########现在来替换要更改的信息
#####用Map集合来传参数
//用map集合来传参数
//1.创建map集合,并用V put(K key, V value) 将指定的值与此映射中的指定键关联 ,用put方法进行存入数据
Map map=new HashMap();
map.put(“id”, 4);
map.put(“name”, “深圳腾”);
map.put(“job”, “TecentCEO”);
map.put(“salary”, 1200000d);
//传入map
int rows = session.update(“EmpMapper.updateById”,map);
session.commit();
System.out.println(rows);
###关于传入的参数,占位符必须中变量名要与属性名保持一致,并且我们优先考虑用类对象进行封装,其次考虑map集合封装。
#{}和${}这是两个占位符
${} 这个是js中的占位符
#{}
####需要注意,再传递KaTeX parse error: Expected 'EOF', got '#' at position 38: …*总结:在大多数情况下还是使用#̲{}占位符,而{}多用于不带引号的字符进行占位**
##4动态SQL (if ,where)
(1) mybatis中的if元素用于对某一字段进行判断,比如根据判断过来的参数是否为空,从而决定是否执行包含在其中的SQL片段。
(2)where元素则用于对包含在其中的SQL语句进行检索,需要时剔除多余的连接词,比如(and或者or),并且需要时可以添加where关键字。
**(3)在mybatis中的set元素用用于对包含在其中的sql语句进行检索,在需要时可与剔除其中多余的连接符(比如逗号),并且在需要时可以添加set关键字 **
ps:
@Test
public void testFindAll4(){
Emp emp=new Emp();
emp.setId(4);
emp.setName("呆鸡贵");
emp.setJob("掏粪大队队长");
emp.setSalary(25000d);
//执行sql语句
int rows = session.update("EmpMapper.testFind4",emp);
//提交
session.commit();
System.out.println("影响的行数:"+rows);
添加后set
<update id="testFind4">
update emp
<set>
<if test="name!=null">
name=#{name},
</if>
<if test="job!=null">
job=#{job},
</if>
<if test="salary!=null">
salary=#{salary}
</if>
</set>
where id=#{id}
</update>
(4)通过forEach标签来批量删除
测试类
//练习:如何通过id进行批量删除
@Test
public void testDelete(){
//进行需要删除的id序号
Integer[] num={1,2,3,8,11};
//执行sql语句
int rows = session.update("EmpMapper.testDelete", num);
//提交事务
session.commit();
System.out.println("影响的行数:"+rows);
}
类对应的xml中的sql语句
<!--通过id进行批量删除
1.delete from emp where in(1,2,4,5,6,9);
2.delete from emp where id=1 or id=2 or id=3...
引进forEach标签,里面有四个属性: collection=” “,如果要变数集合就是list,数组的话就是array
open=” “,开始符号,(
close=” “结束符号,)
item=" ",接收遍历的结果
separator=",",拼接符号:,
-->
<delete id="testDelete">
delete from emp where id in
<foreach collection="array" open="(" item="id" separator="," close=")">
#{id}
</foreach>
</delete>
##5 mybatis中的Mapper接口开发(为了解决)