User.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 命名空间,做SQL隔离-->
<!--用户的增删改查的SQL语句在代码中通过id进行调用,如果表单特别多,每张表单的SQL语句特别多,id可能重复-->
<!--namespace 相当于又加了一层,调用的时候通过namespace加上id进行调用,避免重名的可能-->
<!--namespace起名是有规范的-->
<mapper namespace="test">
<!--具有增删改查对应的子标签-->
<!--id : sql语句唯一标识-->
<!--parameterType:指定传入参数类型,是javaBean中对应属性的类型-->
<!--resultType : 返回结果集类型,如果返回结果为集合,可以调用selectList()方法,这个方法返回的结果就是一个集合,所以映射文件中应该配置成集合泛型的类型-->
<select id="findUserById" parameterType="java.lang.Integer" resultType="cn.zst.domain.User">
<!--#{}占位符,起到占用的作用,如果传入的是基本类型(String long double int Boolean float等)那么 # {}中的变量名称可以随意写-->
SELECT * from `user` WHERE id = #{id}
</select>
<select id ="findUserByName" parameterType="java.lang.String" resultType="cn.zst.domain.User">
<!-- SELECT * from `user` WHERE username like #{name}-->
<!--${}拼接符,字符串原样拼接,如果传入的参数是基本类型(String long double int boolean float等)那么${}中的变量名称必须是value-->
<!--注意:拼接符有SQL注入的风险,所以慎重使用-->
SELECT * from `user` WHERE username like '%${value}%'
</select>
<!--增加-->
<!--如果业务需要返回数据库自增主键,可以使用SELECT LAST_INSERT_ID()-->
<insert id="insertUser" parameterType="cn.zst.domain.User">
<!--执行SELECT LAST_INSERT_ID()数据库函数,返回自增的主键-->
<!--keyProperty:将返回的主键放入传入参数的id中保存,此处的传入参数为User ,-->
<!--order:当前函数相对于insert语句的执行顺序,在insert前执行是before,在insert后执行是after-->
<!--resultType:id 类型 ,也就是在keyproperties中属性的类型-->
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
<!--如果传入的是JavaBean类型,那么#{}中的变量名称必须是JavaBean中对应的属性.属性.属性.属性.属性-->
<!--例如,User中有一个Customer的属性,customer有一个custname属性,则表示为 customer.custname-->
INSERT into `user` (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
<!--删除用户-->
<delete id="delUserById" parameterType="java.lang.Integer">
delete from `user` WHERE id=#{id}
</delete>
<!--更新,根据id来跟新,因为此处传入的是两个参数,所有通过user对象传入,因为它包含所有需要的属性-->
<update id="updateUserById" parameterType="cn.zst.domain.User" >
UPDATE `user` set username=#{username} WHERE id=#{id}
</update>
</mapper>
加载映射文件
<mappers>
<!--url的不用,需要写在绝对路径,如 D:/,,,,-->
<!--resource 相对路径 写完,Ctrl + 左键 可以点进去-->
<mapper resource="User.xml"/>
<!--<mapper resource="UserMapper.xml"/>-->
<!--使用class属性引入接口的全路径名称
使用规则:
1 接口的名称和映射文件名称除扩展名外要完全相同
2 接口和映射文件要放在同一个目录下
-->
<mapper class="cn.zst.mapper.UserMapper" />
</mappers>
测试文件
package cn.zst.test;
import cn.zst.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* Created by zst on 2019/1/8.
*/
public class UserTest {
@Test
public void testFindUserById() throws Exception{
String resource="SqlMapConfig.xml";
//通过流将核心配置文件读取出来
InputStream inputStream = Resources.getResourceAsStream(resource);
//通过核心配置文件输入流来创建会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂创建会话
SqlSession openSession = factory.openSession();
//第一个参数,所调用的SQL语句 = namespace+.+sql的id
User user = openSession.selectOne("test.findUserById", 1);
System.out.println(user);
openSession.close();
}
@Test
public void testFindUserByName() throws Exception{
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession openSession = factory.openSession();
List<User> list = openSession.selectList("test.findUserByName", "王");
System.out.println(list);
openSession.close();
}
@Test
public void testInsertUser() throws Exception{
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession openSession = factory.openSession();
User user = new User();
user.setUsername("王名");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("河南郑州");
openSession.insert("test.insertUser",user);
//提交事务
//如果不提交事务,则数据不能插入到数据库中
//在Hibernate中如果要提交事务,首先要开启事务,beganTransation
//此处Mybatis会自动开启事务,但不知道要什么时候提交事务。所以需要手动提交事务
openSession.commit();
//自增主键的id
System.out.println(user.getId());
}
@Test
public void testDelUserById () throws Exception{
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession openSession = factory.openSession();
openSession.delete("test.delUserById",28);
//提交事务
openSession.commit();
}
@Test
public void testUpdateUserById() throws Exception{
String resource = "sqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession openSession = factory.openSession();
User user = new User();
user.setUsername("王志");
user.setId(24);
openSession.update("test.updateUserById",user);
openSession.commit();
}
}
- #{} 与${}
#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或JavaBean属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
${}表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
- parametertype与resultType
parameterType:指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在sql中。
resultType:指定输出结果类型,mybatis将sql查询结果的一行记录数据映射为resultType指定类型的对象。
- selectOne() 和 selectList()
selectOne查询一条记录,如果使用selectOne查询多条记录则抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectList可以查询一条或多条记录。
mysql 自增主键返回
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<!-- selectKey将主键返回,需要再返回 -->
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address});
</insert>
添加selectKey实现将主键返回
keyProperty:返回的主键存储在pojo中的哪个属性
order:selectKey的执行顺序,是相对与insert语句来说,由于mysql的自增原理执行完insert语句之后才将主键生成,所以这里selectKey的执行顺序为after
resultType:返回的主键是什么类型
LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。
mysql 使用UUID 实现主键
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey resultType="java.lang.String" order="BEFORE"
keyProperty="id">
select uuid()
</selectKey>
insert into user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
<!--注意这里使用的order是“BEFORE”-->
Mybatis 解决JDBC编程的问题
- 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可以解决此问题。
解决:在SqlMapConfig.xml中配置数据链接池,使用链接池管理数据库链接。
- Sql语句卸载代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码
解决:将sql语句配置在XXXXmapper.xml文件与java代码分离> 解决:将sql语句配置在XXXXmapper.xml文件与java代码分离
- 向SQL语句传参数麻烦,因为SQL与语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis 自动将java对象映射至SQL语句,通过statement 中的parameterType定义输入参数的类型
- 对结果集解析麻烦,SQL变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成JavaBean对象解析比较方便
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中resuleType 定义输出结果的类型。
Mybatis与Hibernate的不同
- Mybatis与Hibernate不同,它不完全是一个ORM框架,需要开发人员自己编写SQL语句,不过Mybatis可以通过xml或者注解的方式灵活配置要运行的SQL语句,并将java对象和SQL语句映射生成最终执行的SQL,最后将SQL执行的结果再映射生成java对象。
- Mybatis简单易学,开发人员直接编写原生态SQL,可严格控制SQL执行性能,灵活度高,适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一旦需求变化要求成果输出迅速。但是灵活的前提是Mybatis无法做到数据库无关性,如果需要支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
- Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(如需要固定的定制化软件)如果用Hibernate开发可以节省很多代码,提高效率。