mybatis基础知识
MyBatis是一个实现了数据持久化的开源框架,其实它就像netty之于NIO一样,Mybatis是JDBC的封装,所以:
JDBC的缺点:
-
需要频繁的进行数据库的建立、连接、断开操作,浪费资源,影响数据库性能;
解决:连接池
-
将SQL语句直接编码在Java代码中,需求改变时,需要修改Java代码;
解决:将SQL语句和代码分开,SQL语句放在配置文件中
-
JDBC编程中返回结果集存在硬编码的问题
解决:将结果集映射成Java对象
MyBatis的优点:
-
与JDBC相比,减少了50%以上的代码量。
-
MyBatis是最简单的持久化框架,小巧并且简单易学。
-
MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用。
-
提供XML标签,支持编写动态SQL语句。
-
提供映射标签,支持对象与数据库的ORM字段关系映射,可以把对象映射成表的元组、也可以把表中的元组映射成对象;
MyBatis框架的缺点:
-
SQL语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写SQL语句的功底有一定要求。
-
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
MyBatis和Hibernate的区别:
对于映射层而言,Hibernate的配置不需要接口和SQL,相反MyBatis是需要的,对于Hibernate而言,不需要编写大量的SQL,就可以完全映射,同时提供了日志、缓存、级联(级联比MyBatis强大)等特性,使用十分方便,但同时,Hibernate也存在巨大的缺陷;
MyBatis可以自由的书写SQL、支持动态SQL、处理列表、动态生成表名、支持存储过程,这样就可以灵活的定义查询语句,满足各类需求和性能优化的需要,这些再互联网系统中是十分重要的;
但MyBatis也有缺陷,他要编写SQL和映射规则,其工作量大于Hibernate,其次,它支持的工具也很有限,不能像Hibernate那样有许多的插件可以帮助生成映射代码和关联关系,而即使使用生成工具,往往也需要开发者进行进一步的简化,MyBatis通过手工编码,工作量相对大一些,所以对于性能要求不太苛刻的系统,使用Hibernate较好,对于性能要求高、响应快、灵活的系统则推荐使用MyBatis;
MyBatis与Hibernate相比主要的优势:
MyBatis不屏蔽SQL,这样开发者可以定制自己的SQL,无须自动生成(同时缺点就是工作量大,需要一定的学习成本),这样能够更精确的定义SQL,从而优化性能,符合移动互联网的高并发、大数据、高性能、高响应的要求;
MyBatis框架适用场合:
MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。
Mybatis的使用
先来介绍一下mybatis里面的一些组件吧,由SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory,然后由会话工厂创建会话SqlSession(与JDBC中的Statement类似),会话就是一个用户接口(他是不安全的,不能共用,所以一般都会在方法里面,作为一个局部变量),用户就可以那这个SqlSession使用他来操作数据库了;
上面介绍过,MyBatis能提供表元组和对象之间的映射,所以需要建立一张表,这里用Student:
create table Student(
SID int(10),
Sname varchar(20),
Ssex varchar(10),
Age int(10)
);
然后在这个表里面添加一些数据,以便后面用来测试,随便填写几个就可以了:
MyBatis开发有两种方式:
使用配置或使用注解;
配置实现
使用步骤:
- 导入依赖;
- 创建bean类、dao层接口;
- 创建Mapper的xml文件、创建mybatis的xml文件;
- 使用;
一、导入依赖
1、导入mybatis依赖:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
2、还需要数据库的依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
二、创建bean类、Mapper接口
bean类Student:
public class Student {
private int SID;
private String Sname;
private String Ssex;
private int Age;
public int getSID() {
return SID;
}
public void setSID(int SID) {
this.SID = SID;
}
public String getSname() {
return Sname;
}
public void setSname(String sname) {
Sname = sname;
}
public String getSsex() {
return Ssex;
}
public void setSsex(String ssex) {
Ssex = ssex;
}
public int getSage() {
return Age;
}
public void setSage(int sage) {
Age = sage;
}
@Override
public String toString() {
return "[id"+SID+" 名字"+Sname+" 性别"+Ssex+" 年龄"+Age+"]";
}
}
需要先创建自定义接口,但是不需要实现该接口,通过Mapper代理来实现:
如下,创建StudentMapper接口:
public interface StudentMapper {
public Student getStudentById(int id); //根据给定id查找并返回对象
}
三、创建两个xml文件
创建StudentMapper.xml文件,也放在resources下面,如上图所示,文件内容如下:
先说明:
- select标签,和数据库的sql语句类似,这个表示查询,对应的还有update、delete、insert;
- id:除过使用id还可以使用name,name里面可以含有特殊字符,而id里面不能,表示一个代号或者说标识吧;
- parameter:入参类型,包括parameterType和parameterMap,参数类型可以是基本数据类型、自定义数据类型、Map类型;
- result:返回参数的类型,包括resultType和resultMap,参数类型和和上面一样;
<?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">
<mapper namespace="com.tulun1.dao.StudentMapper">
<select id="getStudentById" parameterType="java.lang.Integer" resultType="com.tulun1.bean.Student">
select * from student where SID = #{SID}
</select>
</mapper>
statement标签根据SQL执行的业务可选择insert,delete,update,select。
MyBatis会根据规则自动创建UserDAO接口实现类的代理对象。
规则如下:
- Mapper.xml 中 namespace 为Mapper.java接口的全路径名。
- Mapper.xml中 statement 的 id 为Mapper.java接口中对应的方法名。
- Mapper.xml中 statement 的 parameter和Mapper.java接口中对应方法的参数类型一致。
- Mapper.xml中 statement 的 result 和Mapper.java接口中对应方法的返回值类型一致。
补充:
- 不管是单个对象还是列表,在mapper.xml中的result的类型是一样的(但是他们的底层调用的方法不同,由mabatis处理,单个对象调用的是selectOne()方法,而列表调用的是selectList()),在对应的mapper.java接口中的返回值不一样;
- resultType适用于自定义类型属性和数据库表字段一样,而resultMap一般适用于不一样的情况,包括多表联合查询(即返回新的数据类型的时候);
添加MyBatis全局配置文件config.xml(文件名可自定义,这里用的mybatis-config.xml)
注意使用自己的数据库名字、自己的root用户、密码:
<?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>
<!--数据源配置-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--映射-->
<mappers>
<mapper resource="配置使用mybatis/StudentMapper.xml"/>
</mappers>
</configuration>
注意,这个xml文件放在与Java文件同级的resources(名字可以自己起,但必须是配置文件,就是文件夹图标下面有4个黄色的横岗)文件下面:
下面是一个mybatis的配置文件(没用他,只是认识一些常用标签):
<?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>
<!--配置数据源-->
<properties resource="druid.porperties"/>
<!--全局配置信息-->
<settings>
<!--打印sql语句-->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!--开启二级缓存-->
<setting name="cacheEnable" value="true"/>
<!--懒加载-->
</settings>
<!--类型别名 两种写法-->
<typeAliases>
<!--对单个的自定义对象取别名-->
<!--type是对象的全路径 alias是别名-->
<typeAlias type="com.tulun3" alias="3"/>
<!--批量取别名-->
<!--name指定的为要取别名的包目录位置 默认的把类名当别名-->
<package name="com.tulun3"/>
</typeAliases>
<!--对象工厂-->
<objectFactory type=""/>
<plugins>
<!--插件-->
<plugin interceptor=""></plugin>
</plugins>
<!--数据源配置-->
<environments default="development">
<environment id="development">
<!--transactionManager处理事务-->
<transactionManager type="JDBC"/>
<!--数据源配置-->
<dataSource type="POOLED">
<!--当通过读取配置文件来初始化数据源时用该写法-->
<property name="driver" value="#{driver}"/>
<property name="url" value="#{url}"/>
<property name="username" value="#{username}"/>
<property name="password" value="#{password}"/>
</dataSource>
</environment>
</environments>
<!--映射器 三种写法-->
<mappers>
<!--1、resource方式 一次只能加载一个文件
maper.xml和maper.java不需要在同一个目录下面,名字也不需要相同-->
<mapper resource="RoleMapper.xml"/>
<!--2、class方式 一次只能加载一个文件
maper.xml和maper.java需要在同一个目录下面,名字也需要相同-->
<mapper class="com.tulun3"/>
<!--3、package方式 批量加载
maper.xml和maper.java需要在同一个目录下面,名字也需要相同-->
<package name="com.tulun3"/>
</mappers>
</configuration>
四、使用
测试代码:
我用的junit,所以需要这个jar包:
<!--test测试用的jar包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
public class TestDemo1 {
@Test
public void test() throws IOException {
String resource = "配置使用mybatis/mybatis-config.xml";
//读取配置文件
InputStream asStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理产生StudentMapper对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.getStudentById(2); //获取id为2的数据的对象
System.out.println(student);
}
}
注解实现
使用步骤:
- 导入依赖;
- 创建对应的bean类、dao层接口;
- 创建Mapper的xml文件、mybatis的全局配置xml文件;
- 使用;
一、导入依赖
和上面一样,略;
二、创建对应的bean类、dao层接口
bean类和上面一样,略,dao层接口需要添加注解,如下(对应的增删该查方法我都写了):
public interface StudentMapper {
@Select("select * from student where SID = #{id}")
public Student getStudentById(int id);
@Select("select * from student where Sname = #{name} and Age = #{age}")
public Student getSnameAndSage(@Param("name") String name, @Param("age") int age);
@Select("select * from student")
public Student[] getAllStudent();
@Insert("insert into student (SID,Sname) values(#{SID},#{Sname})")
public void addStudent(Student student);
@Delete("delete from student where Sid = #{id}")
public void delete(int id);
@Update("update student set Sname = #{name} where id=#{id}")
public void setValue(@Param("id") int id,@Param("name") String name);
}
注意:在参数比较多的时候,需要在接口参数类型前面添加@Param(参数名称),否者它就不知道哪个参数放在sql语句的哪个地方了,这样可以指定这个参数是给哪的;
三、创建两个xml文件
上面基本一样,只不过SQL语句不用写到StudentMapper.xml里面,在StudentMapper.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">
<mapper namespace="com.tulun2.dao.StudentMapper">
</mapper>
mybatis的xml文件也不需要修改,略;
测试代码:
public class TestDemo1 {
@Test
public void test() throws IOException {
String resource = "注解使用mybatis/mybatis-config.xml";
//读取配置文件
InputStream asStream = Resources.getResourceAsStream(resource);
//创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);
//创建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理产生StudentMapper对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//查询id为2的元组
Student student = mapper.getStudentById(2);
System.out.println("=============查询id=2的记录======================");
System.out.println(student);
//往数据库添加新的元组
Student student1 = new Student();
student1.setSID(4);
student1.setSname("xxx");
mapper.addStudent(student1);
sqlSession.commit();
Student student2 = new Student();
student1.setSID(9);
student1.setSname("xxx");
mapper.addStudent(student2);
sqlSession.commit();
//返回列表所以元素
System.out.println("=============添加两条记录后所有的记录======================");
Student[] students1 = mapper.getAllStudent();
for (Student s : students1) {
System.out.println(s);
}
//删除
mapper.delete(4);
mapper.delete(9);
sqlSession.commit();
System.out.println("=============删除两条记录后所有的记录======================");
Student[] students2 = mapper.getAllStudent();
for (Student s : students2) {
System.out.println(s);
}
}
}
气人,不知道为什么多出一条0的记录!!!
#{}和${}的区别
顺便说一下,在配置文件里面,SQL语句的参数可以是#{}
还可以是${}
,这两者有什么区呢?
要看区别(后面几节也会用到),需要导入如下三个日志相关的依赖:
<!--打印日志 打印sql语句-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
然后需要一个 log4j 的配置文件 log4j.properties:
## debug 级别
log4j.rootLogger=DEBUG,Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{yyyy-MM-dd-HH\:mm\:ss,SSS} [%t] [%c] [%p] - %m%n
log4j.logger.com.mybatis=DEBUG /
##输出sql 语句
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG</strong>
把它配置文件的根目录下面:
最后就是在我们要用的mybatis的配置文件里面添加:
<!--全局配置信息-->
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="LOG4J" />
</settings>
就是上面我们用的mybatis.xml文件里面添加:
#{}
相当于JDBC里面的PreparedStatement ;先编译,编译没问题再拿这个编译的结果去执行:
再次运行上面的代码:
${}
相当于JDBC里面的Statement;直接拿,然后编译执行;
所以相比之下,#{}效率更高,并且不存在SQL注入问题,比较安全;