MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
持久化:就是将程序的数据从瞬时状态向持久状态转化的过程;
瞬时状态:内存中的数据是瞬时状态的数据;
持久状态:数据库、io文件中的数据是持久状态的数据;
MyBatis SQL Mapper Framework for Java;功能包括:
sql映射:表中的一行数据映射为一个java对象;
数据访问(DAO):对数据库执行增删改查;
mybatis封装了connection,statement,resultset,及其关闭方法,提供了循环sql结果集的能力,开发人员只需要提供sql语句即可;
2.mapper文件dao/接口名.xml
3.主配置文件-初步select * from y_movie insert into y_movie(m_name,m_director,m_up_year,m_adddate) values(#{m_name},#{m_director},#{m_up_year},#{m_adddate})
resources/mybatis.xml
4.查询/新增数据-初步
public static void main(String[] args) throws IOException {
// 访问mappers文件读取student数据
// 1.获取mybatis主配置文件的名称,从类文件的根目录开始(target/classes/)
String config = "mybatis.xml";
// 2.读取文件
InputStream in = Resources.getResourceAsStream(config);
// 3.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 4.创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);
// 5.从SqlSessionFactory中获取SqlSession对象
SqlSession sqlSession = factory.openSession();
// 6.获取要执行的Sql语句的标识,sql映射文件的namespace+"."+标签id
String sqlId = "org.mybatisTest.dao.MovieDAO.selectMovie";
// 7.执行sql语句
List listMovie = sqlSession.selectList(sqlId);
// 输出结果
listMovie.forEach(ms -> System.out.println(ms.getM_name()));
sqlSession.close();
};
//新增
public static void Add() throws IOException {
// 访问mappers文件读取student数据
// 1.获取mybatis主配置文件的名称,从类文件的根目录开始(target/classes/)
String config = "mybatis.xml";
// 2.读取文件
InputStream in = Resources.getResourceAsStream(config);
// 3.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 4.创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);
// 5.从SqlSessionFactory中获取SqlSession对象
SqlSession sqlSession = factory.openSession();
// 6.获取要执行的Sql语句的标识,sql映射文件的namespace+"."+标签id
String sqlId = "org.mybatisTest.dao.MovieDAO.insertMovie";
// 7.执行sql语句
Movie m = new Movie();
Date m_adddate = new Date();
m.setM_adddate(m_adddate);
m.setM_name("战争之王2");
m.setM_up_year(2016);
m.setM_director("詹姆斯2");
int count = sqlSession.insert(sqlId, m);
// sqlSession默认不会自动提交,需要手动提交事务;
sqlSession.commit();
// 输出结果
System.out.println(count);
}
5.mybatis主配置文件设置日志
必须配置,对代码调试非常重要;
6.主要类的介绍
Resources: mybatis中的一个类,负责读取主配置文件
InputStream in = Resources.getResourceAsStream(config);
SqlSessionFactoryBuilder : 创建SqlSessionFactory对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in);
SqlSessionFactory : 用来获取SqlSession对象,重量级对象,该对象创建耗时较长,占用资源较多,在整个项目中,有一个就够了;
SqlSession sqlSession = factory.openSession();
openSession方法:
//true表示获取自动提交事务的sqlSession,默认为false factory.openSession(boolean);
SqlSession:接口,定义了操作数据库的方法,selectOne,selectList,insert,update....
SqlSession实现类:DefaultSqlSession;
SqlSession是非线程安全的,需要在方法内部使用,在执行sql语句之前,使用openSession获取SqlSession,在执行完sql语句之后需要关闭他,SqlSession.close();这样才能保证他是线程安全的;
7.封装SqlSessionpublic class MybatisUtils {
private static SqlSessionFactory factory = null;
static {
// mybatis主配置文件
String config = "mybatis.xml";
InputStream in;
try {
in = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
if (factory != null) {
sqlSession = factory.openSession();
}
return sqlSession;
}
}
8.编写DAO接口与实现类与mybatis的动态代理
//接口操作Movie表
public interface MovieDAO {
// 查询movie表所有数据
public List selectMovie();
public int insertMovie(Movie m);
}
//Movie实现接口
public class MovieDAOImp implements MovieDAO {
@Override
public List selectMovie() {
// TODO Auto-generated method stub
SqlSession sqlSession = MybatisUtils.getSqlSession();
String sqlId = "org.mybatisTest.dao.MovieDAO.selectMovie";
List movieList = sqlSession.selectList(sqlId);
sqlSession.close();
return movieList;
}
@Override
public int insertMovie(Movie m) {
// TODO Auto-generated method stub
SqlSession sqlSession = MybatisUtils.getSqlSession();
String sqlId = "org.mybatisTest.dao.MovieDAO.insertMovie";
int c = sqlSession.insert(sqlId, m);
sqlSession.commit();
sqlSession.close();
return c;
}
}
最终简化查询与新增
public static void Add() throws IOException {
Movie m = new Movie();
m.setM_name("那些年");
m.setM_director("赵薇");
m.setM_up_year(2011);
m.setM_adddate(new Date());
MovieDAO movieImp = new MovieDAOImp();
int c = movieImp.insertMovie(m);
// 输出结果
System.out.println(c);
}
public static void Select() throws IOException {
MovieDAO movieImp = new MovieDAOImp();
List listMovie = movieImp.selectMovie();
// 输出结果
listMovie.forEach(ms -> System.out.println(ms.getM_name()));
}
可以继续优化的地方:其实从调用方法语句中就可以通过反射获取两个信息:
//多态 MovieDAO movieImp = new MovieDAOImp(); movieImp.insertMovie(m);
movieImp对象的全类名:org.mybatisTest.dao.MovieDAO,与之调用的方法对应的mapper.xml文件中的对应的sql语句的id值(通过全类名与sqlID就能确定一条唯一的sql语句);所以实现类内部中的sqlId代码是可以被省略的,是冗余的;
String sqlId = "org.mybatisTest.dao.MovieDAO.insertMovie";
同时实现类内部的获取sqlSession,close、commit都是可以被提取出来简化的,所以这部分代码也是可以省略的;
以上可知dao接口的实现类是可以省略的,所以mybatis的动态代理就帮我们省略了DAO接口的实现类,并帮我们创建了DAO实现类的对象;
mybatis的动态代理:
public static void Test2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
MovieDAO movieDAO = sqlSession.getMapper(MovieDAO.class);
List movieList = movieDAO.selectMovie();
for (Movie movie : movieList) {
System.out.println(movie.getM_name());
}
//添加
Movie m = new Movie();
System.out.println(movieDAO.getClass().getName());
m.setM_name("大红灯笼高高挂");
m.setM_director("张艺谋");
m.setM_up_year(1995);
int c = movieDAO.insertMovie(m);
sqlSession.commit();
sqlSession.close();
}
mybatis动态代理可以帮我们创建DAO接口的实现类,在实现类中调用sqlSession的方法执行sql语句;
实现动态代理的要求:
①mapper文件的namespace是DAO接口的全类名;
②mapper文件的标签id是DAO接口的方法名;
③mapper文件名与DAO接口文件名一致;
④DAO接口中不能使用重载方法,即不能使用同方法名,参数不同的方法;
9.简单参数//mapper文件
parameterType:dao接口中方法参数的数据类型,值是java数据类型的全类名或者mybatis定义的别名,mybatis通过反射能够得到接口参数的数据类型,所以这个参数可以不写,一般也不写;
mybatis把java基本数据类型和String类型都称为简单数据类型;
在mapper文件中可以用#{字符串}获取简单数据类型的值;
10.多个参数@param方式:
当DAO接口的方法中有多个参数,需要通过名称传递参数,可以在方法的形参的前面加@param("自定义参数名")传参,mapper文件通过#{自定义参数名}获取参数的值;
//DAO接口方法
public int updateMovie(@Param("u_id") Integer id, @Param("u_name") String name, @Param("u_year") Integer year);
//mapper文件
update y_movie set m_name=#{u_name},m_up_year=#{u_year} where m_id=#{u_id}
//调用
public static void Test4() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
MovieDAO mDao = sqlSession.getMapper(MovieDAO.class);
int c = mDao.updateMovie(20, "钢铁侠", 2013);
System.out.println(c);
sqlSession.commit();
sqlSession.close();
}
推荐使用该方式传多个值;
对象属性方式:
整型在jdbcType是INTEGER或者NUMERIC;语法
#{对象属性名,javaType=对象属性的java的属性类型,jdbcType=对象属性对应的数据库类型},这是完整写法,一般javaType与jdbcType可以通过的java的反射获取,所以可以不写;
可以简化成#{对象属性名}
//创建对象
public class Movie {
private int m_id;
private String m_name;
private String m_director;
private int m_up_year;
private Date m_adddate;
public int getM_id() {
return m_id;
}
public void setM_id(int m_id) {
this.m_id = m_id;
}
public String getM_name() {
return m_name;
}
public void setM_name(String m_name) {
this.m_name = m_name;
}
public String getM_director() {
return m_director;
}
public void setM_director(String m_director) {
this.m_director = m_director;
}
public int getM_up_year() {
return m_up_year;
}
public void setM_up_year(int m_up_year) {
this.m_up_year = m_up_year;
}
public Date getM_adddate() {
return m_adddate;
}
public void setM_adddate(Date m_adddate) {
this.m_adddate = m_adddate;
}
}
//DAO接口方法
public int updateMovie2(Movie m);
//mapper映射语句
update y_movie set m_name=#{m_name},m_director=#{m_director} where m_id=#{m_id}
update y_movie set m_name=#{m_name,javaType=java.lang.String,jdbcType=VARCHAR},m_director=#{m_director,javaType=java.lang.String,jdbcType=VARCHAR} where m_id=#{m_id,javaType=java.lang.Integer,jdbcType=INTEGER}
//调用
public static void Test5() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
MovieDAO dao = sqlSession.getMapper(MovieDAO.class);
Movie obj = new Movie();
obj.setM_name("机器人瓦力2");
obj.setM_id(15);
obj.setM_director("安德鲁·斯坦顿");
int c = dao.updateMovie2(obj);
sqlSession.commit();
System.out.println(c);
sqlSession.close();
}
根据位置传参:
//DAO接口方法 public int updateMovie3(String name, String director, Integer id); //mapper文件update y_movie set m_name=#{arg0},m_director=#{arg1} where m_id=#{arg2} //调用 public static void Test6() { SqlSession sqlSession = MybatisUtils.getSqlSession(); MovieDAO dao = sqlSession.getMapper(MovieDAO.class); int c = dao.updateMovie3("神秘代码", "亚历克斯·普罗亚斯", 15); sqlSession.commit(); System.out.println(c); sqlSession.close(); }
不符合见名知意原则,一般不采用;
map方式传参:
//DAO接口方法 public int updateMovie4(Mapmap); //mapper文件 update y_movie set m_name=#{movieName},m_director=#{movieDirector} where m_id=#{movieId} //调用 public static void Test7() { SqlSession sqlSession = MybatisUtils.getSqlSession(); MovieDAO dao = sqlSession.getMapper(MovieDAO.class); Mapmap = new HashMap (); map.put("movieName", "敦刻尔克"); map.put("movieId", 14); map.put("movieDirector", "克里斯托弗·诺兰"); int c = dao.updateMovie4(map); sqlSession.commit(); System.out.println(c); sqlSession.close(); }
map集合作为参数,没有显式定义key与value的数据类型,定义变量不够清晰、全面、可读性差,所以一般不推荐使用;
11.#{}与${}的区别#{}是占位符,是将传入的值当做字符串的形式,是以预编译的形式,将参数设置到sql语句中;相当于PreparedStatement预编译语句里面的?,是使用PreparedStatement执行sql语句 ,可以防止sql注入;
${}是纯粹的字符串替换,一般用于字符串的拼接与替换,是使用Statement执行sql语句,例如在order by ${column},此时只能用${};
${}有sql注入的风险一般能用#{}就不用${};
12.ResultType结果类型ResultType指定sql语句执行完毕后,数据转换的java对象;
处理过程:
①mybatis执行sql语句,然后调用类的无参构造对象,创建对象;
②mybatis根据ResultSet的列名,赋值给与列名相同名称的对象属性;
ResultType可以不是实体类,比如返回视图对象;
//ViewMovie
public class MovieView {
private int m_id;
private String m_name;
private String m_director;
public int getM_id() {
return m_id;
}
public void setM_id(int m_id) {
this.m_id = m_id;
}
public String getM_name() {
return m_name;
}
public void setM_name(String m_name) {
this.m_name = m_name;
}
public String getM_director() {
return m_director;
}
public void setM_director(String m_director) {
this.m_director = m_director;
}
}
//DAO方法
public List selectMovie3();
//mapper文件
//调用
public static void Test9() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
MovieDAO m = sqlSession.getMapper(MovieDAO.class);
List movies = m.selectMovie3();
movies.forEach(ms -> System.out.println(ms.getM_name()));
}



