mybatis的概述
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射,它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集,它可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
框架分类
1) 持久层框架 (与数据库打交道)
ORM 框架 object(java对象) relationship (关系型数据库 mysql) mapping (映射-java对象和数据库之间)持久层框架有:mybatis(大型项目-直接管理sql),hibernate(小型项目-不需要直接写sql语句),jpa,jooq ...
2) mvc 框架(跟 web 应用程序打交道)
mvc 框架 Model(模型-数据) View(视图-数据展现方式) Controller(控制器)把模型数据准备好, 把数据放入作用域, 最后通过jsp视图展现数据
mvc 框架:springmvc, struts2
3) spring 框架 (容器技术)
把各种框架进行集成,让他们协同工作
配置
- 添加依赖( pom.xml 中添加 mybatis 依赖)
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
- 添加配置文件 mybatis-config.xml(resources/mybatis-config.xml )
用这个配置文件来告诉 mybatis 如何连接数据库,告诉 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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!-- 配置了 mybatis 连接数据库的信息 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/数据库名?useSSL=false"/> <property name="username" value="数据库用户名"/> <property name="password" value="数据库密码"/> </dataSource> </environment> </environments> <mappers> <!-- 指定映射文件的位置,以下两个xml文件用第一个,反之则第二个 如:mapper/StudentMapper.xml--> <mapper resource="映射文件的位置"/> <mapper class="接口 mapper的 包名.类名"/> </mappers> </configuration>
- 提供一个映射文件
用来管理 sql 语句,描述 sql 语句与数据库表之间的映射关系 。
<!-- namespace(命名空间) 用来防止 sql 的命名冲突的 --> <mapper namespace="命名空间"> <insert id="sql的名字" parameterType="参数类型" useGeneratedKeys="true" keyProperty="主键对应的属性"> <update id="sql的名字" parameterType="参数类型"> <delete id="sql的名字" parameterType="参数类型"> <select id="sql的名字" parameterType="参数类型" resultType="结果类型,如果是集合是集合中元素的类型"> <if test="条件"> sql 片段 </if> <set></set> </mapper> 参数类型: int, string, #{名字任意} map #{key} 自定义javabean #{属性名}
如:
<!-- namespace(命名空间) 也就是xml(写着SQL语句的xml)的位置 用来防止 sql 的命名冲突的 --> <mapper namespace="mapper.StudentMapper"> 新增 <!-- #{sname} 用来获取 Student 参数对象中的 sname属性--> <!-- useGeneratedKeys="true" 是告诉 mybatis 要使用由数据库产生的主键值 --> <!-- keyProperty="主键对应的属性名" --> <insert id="abc" parameterType="domain.Student" useGeneratedKeys="true" keyProperty="sid"> insert into student(sid, sname, birthday, sex) values ( null, #{name}, #{birthday}, #{sex}) </insert> 删除 <delete id="delete" parameterType="int"> delete from student where sid = #{sid} </delete> 查询所有 <select id="findAll" resultType="domain.Student"> select sid,sname name,birthday,sex from student </select> 根据id查询 <select id="findById" resultType="domain.Student" parameterType="int"> select sid,sname name,birthday,sex from student where sid = #{sid} </select> 更新 <update id="update" parameterType="domain.Student"> update student set sname=#{name}, birthday=#{birthday}, sex=#{sex} where sid=#{sid} </update> 用 set 标签可以去除多余的逗号(动态更新列) <update id="update" parameterType="domain.Student"> update student <set> <if test="name != null"> sname=#{name}, </if> <if test="birthday != null"> birthday=#{birthday}, </if> <if test="sex != null"> sex=#{sex}, </if> </set> where sid=#{sid} </update> </mapper>
通过日志工具监控mybatis生成的sql语句
logback 利用命名空间可以控制哪些sql会作为日志被输出。
- 添加依赖
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
- 添加一个 logback.xml 到 resources文件夹
<?xml version="1.0" encoding="UTF-8"?> <configuration xmlns="http://ch.qos.logback/xml/ns/logback" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd"> <!-- 输出控制,格式控制--> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{32} - %m%n </pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 日志文件名称 --> <file>logFile.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每天产生一个新的日志文件 --> <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 保留 15 天的日志 ,根据需求更改--> <maxHistory>15</maxHistory> </rollingPolicy> <encoder> <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{17} - %m%n </pattern> </encoder> </appender> <!-- 用来控制查看那个类的日志内容(对mybatis name 代表命名空间) --> <logger name="命名空间,也就是xml(写着SQL语句的xml)的位置" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </logger>
mybatis api
- MyBatis 中重要的接口和类
SqlSessionFactory (接口) - 创建 sqlsession 的
SqlSessionFactoryBuilder (类) - 创建工厂对象
SqlSession (接口) - 执行增删改查,管理事务代码实现如下:
InputStream in = Resources.getResourceAsStream("配置文件的路径");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = factory.openSession();
- sqlSession 中重要的方法
sqlSession
.insert("命名空间+sql的名字", sql需要的参数对象)
.update("命名空间+sql的名字", sql需要的参数对象)
.delete("命名空间+sql的名字", sql需要的参数对象)
.selectOne("命名空间+sql的名字", sql需要的参数对象) 结果只有一个时
.selectList("命名空间+sql的名字", sql需要的参数对象) 结果是list集合对增删改
sqlSession.commit();关闭
sqlSession.close();
动态 sql 生成
- foreach 循环
普通SQL(写死)delete from 表 where id in(1008,1011,1012);
动态
<!-- list (1008,1011,1012, 1013) -->
<delete id="" parameterType="list">
delete from 表 where id in
</delete><!-- open表示集合里开始遍历的标志,close表示结束标志,separater表示分隔符 -->
<foreach collection="集合" item="临时变量名" open="(" close=")" separater=",">
- if where 生成动态sql
<where> </where>能够生成 where 关键字,并去除多余的 and
<if test="条件"> sql 片段 </if>如:
<select id="findAll" resultType="domain.Student">
select * from a
<where>
<if test="id=5"> id=#{id} and</if>
<if test="name=张三"> name=#{name} and</if>
<if test="sex=男"> sex=#{sex} and</if>
</where>
</select>
如果三个火两两成立的话,会多出一个and,这是where标签会自动排除多余的and
- 对于大于小于条件的处理
方法1: 对每个特殊字符转转义,例如 < < > >
方法2: <![CDATA[ 内容 ]]>
#{ } 与 ${ } 的区别
- #{ } 底层是替换为 ?, 只能占位值,相对安全,只能代替值,不能运算
- ${ } 底层是直接拼接字符串, 有注入攻击问题,可以做简单运算,可以代替sql中的任意部分
- 尽量使用 #{ } , 只有在某些功能不能用 #{ } 实现时(例如排序)采用 ${ }
Mapper 接口(接口方式的映射)
- 接口方式的映射
@Insert ("sql")
@Option("sql")
@Update("sql")
@Delete("sql")
@Select("sql")
- 原理
// 这个类是使用了 jdk的动态代理技术 在代码运行期间生成的类
public class $Proxy10 implements StudentMapper{
private SqlSession sqlSession;
public $Proxy10(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
// 其中 sql语句从@Insert注解获得, 参数对象就是student
public void insert(Student student) {
sqlSession.insert(sql, 参数对象)
}// 其中 sql语句从@Select注解获得
public List<Student> findAll() {
return sqlSession.selectList(sql);
}
}
Mapper接口不足
1. 接口方法不能直接应用多个方法参数
解决方法:
1) 用map传递多个参数, 每个参数对应map中的一个键值对
2) 用@Param注解2. Mapper 接口中不能有方法的重载
定义方法是不能方法名冲突
异常信息:
Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for 接口名.方法名(重复的)3. 使用Mapper接口方式实现动态sql比较复杂
方法1: 结合Mapper接口和xml文件
复杂sql映射
除了使用 xml mapper 以外,还可以利用 @DeleteProvider @InsertProvider @SelectProvider @UpdateProvider生成复杂sql
@DeleteProvider(
//这个文件里有该方法,方法的实现在这个文件中
type = java文件.class,
method = 方法名
)
void deleteByIds(List<Integer> ids);
//如下:由 @DeleteProvider 注解找到一条sql语句,供 deleteByIds 方法使用
@DeleteProvider(
type = StudentSqlProvider.class,
method = "getDeleteByIdsSql"
)
void deleteByIds(List<Integer> ids);
高级映射
列别名与属性不一致的情况下:
- 方法一:给列起一个列别名
- 方法二:使用 `<resultMap>`
缓存
- 一级缓存
sqlsession 自带缓存功能,缓存没有才查库,缓存中有,直接返回缓存的结果,称为一级缓存,在sqlsession创建时建立,sqlsession.close 时清空,每个sqlsession有自己独立的缓存
- 二级缓存
需要额外设置,但可以允许多个 sqlsession 共享缓存数据,二级缓存中的对象需要实现序列化接口
配置
xml mapper 二级缓存的配置: 在 xml 映射文件中添加`<cache/>`标签即可
接口 mapper 二级缓存的配置: 在接口上添加 @CacheNamespace 注解
好处: 可以较大提升查询效率, 但是增删改频繁的情况下,不适合开启二级缓存。
代码演示
1、xml文件形式的增删改
目录结构
存储学生信息的代码
import java.util.Date;
public class Student {
private int sid;
private String name;
private Date birthday;
private String sex;
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + name + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
'}';
}
}
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">
<!-- namespace(命名空间) 用来防止 sql 的命名冲突的 也就是防止相同文件名,分不清的情况,因此标上包名来分辨-->
<mapper namespace="mapper.StudentMapper">
<!-- #{sname} 用来获取 Student 参数对象中的 sname属性-->
<!-- useGeneratedKeys="true" 是告诉 mybatis 要使用由数据库产生的主键值 -->
<!-- keyProperty="主键对应的属性名" -->
<!--parameterType表示#{}中数据的类型,这里插入代码花括号里面的数据是Student类型,因此写上domain.Student(写上包名)-->
<insert id="abc" parameterType="domain.Student"
useGeneratedKeys="true" keyProperty="sid">
insert into student(sid, sname, birthday, sex)
values ( null, #{name}, #{birthday}, #{sex})
</insert>
<!--#{sid}是学号,学号是int类型,因此parameterType="int"-->
<delete id="delete" parameterType="int">
delete from student where sid = #{sid}
</delete>
<select id="findAll" resultType="domain.Student">
select sid,sname name,birthday,sex from student
</select>
<select id="findById" resultType="domain.Student" parameterType="int">
select sid,sname name,birthday,sex from student where sid = #{sid}
</select>
<!-- 更新所有列
<update id="update" parameterType="domain.Student">
update student set sname=#{name}, birthday=#{birthday}, sex=#{sex} where sid=#{sid}
</update>
-->
<!-- 动态更新列
Student stu = new Student();
stu.setSid(1001);
stu.setSex("男");
update student set sex=#{sex} where sid=#{sid}
用 set 标签可以去除多余的逗号
-->
<update id="update" parameterType="domain.Student">
update student
<set>
<if test="name != null">
sname=#{name},
</if>
<if test="birthday != null">
birthday=#{birthday},
</if>
<if test="sex != null">
sex=#{sex},
</if>
</set>
where sid=#{sid}
</update>
<!-- m , n
java.util.Map -> map
java.util.List -> list
java.lang.String -> string
map.put("m", 0);
map.put("n", 5);
resultType="domain.Student"表示结果的类型,因为要查的是学生的信息,因此类型是Student类型
-->
<select id="findByPage" parameterType="map" resultType="domain.Student">
select sid,sname name,birthday,sex from student limit #{m}, #{n}
</select>
</mapper>
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">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 配置了 mybatis 连接数据库的信息 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 指定映射文件的位置 ,也就是有增删改内容的xml文件的位置-->
<mapper resource="mapper/StudentMapper.xml"/>
</mappers>
</configuration>
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns="http://ch.qos.logback/xml/ns/logback"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">
<!-- 输出控制,格式控制-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{32} - %m%n </pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志文件名称 -->
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天产生一个新的日志文件 -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保留 15 天的日志 -->
<maxHistory>15</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{17} - %m%n </pattern>
</encoder>
</appender>
<!-- 用来控制查看那个类的日志内容(对mybatis name 代表命名空间) 这和前面有增删改内容的xml文件的命名空间一样的意思-->
<logger name="mapper.StudentMapper" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</logger>
<root level="ERROR">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
测试代码
import domain.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
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.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestStudentMapper {
static SqlSessionFactory factory;
static {
// 读取配置文件
try(InputStream in = Resources.getResourceAsStream("mybatis-config.xml")){
// 创建 sqlSession 工厂类
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testInsert() throws IOException {
// 创建 sqlSession, 这里的 session 更类似于 jdbc 中的 Connection
SqlSession sqlSession = factory.openSession();
// 执行新增
Student stu = new Student();
stu.setName("李五");
stu.setBirthday(new Date());
stu.setSex("男");
// 参数1 : namespace+sql_id , 参数2: 传递sql语句需要的java对象
sqlSession.insert("mapper.StudentMapper.abc", stu );
// 执行 增删改, 没有启用自动提交事务
sqlSession.commit();
System.out.println("sid的值:" + stu.getSid());
// 关闭资源
sqlSession.close();
}
@Test
public void testDelete() {
SqlSession sqlSession = factory.openSession();
sqlSession.delete("mapper.StudentMapper.delete",
1009);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testFindAll() {
// selectList 用在查询多个结果
SqlSession sqlSession = factory.openSession();
List<Student> list = sqlSession.selectList("mapper.StudentMapper.findAll");
sqlSession.close();
for (Student student : list) {
System.out.println(student);
}
}
@Test
public void testFindById() {
// selectOne 只能用在查询结果唯一的情况下,如果结果有多个,selectOne会报异常
SqlSession sqlSession = factory.openSession();
Student stu = sqlSession.selectOne("mapper.StudentMapper.findById",
1001);
sqlSession.close();
System.out.println(stu);
}
@Test
public void testUpdate() {
SqlSession sqlSession = factory.openSession();
Student stu = new Student();
stu.setSex("男");
stu.setSid(1001);
sqlSession.update("mapper.StudentMapper.update", stu);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testFindByPage() {
SqlSession sqlSession = factory.openSession();
Map<String, Integer> map = new HashMap<>();
map.put("m", 0);
map.put("n", 5);
List<Student> list = sqlSession.selectList("mapper.StudentMapper.findByPage", map);
for (Student student : list) {
System.out.println(student);
}
}
}
结果
testInsert()的结果
2019-02-16 22:41:18 [DEBUG] mapper.StudentMapper.abc - ==> Preparing: insert into student(sid, sname, birthday, sex) values ( null, ?, ?, ?)
2019-02-16 22:41:19 [DEBUG] mapper.StudentMapper.abc - ==> Parameters: 李五(String), 2019-02-16 22:41:13.8(Timestamp), 男(String)
2019-02-16 22:41:21 [DEBUG] mapper.StudentMapper.abc - <== Updates: 1
sid的值:1009
testDelete()的结果
2019-02-16 22:51:16 [DEBUG] mapper.StudentMapper.delete - ==> Preparing: delete from student where sid = ?
2019-02-16 22:51:17 [DEBUG] mapper.StudentMapper.delete - ==> Parameters: 1009(Integer)
2019-02-16 22:51:17 [DEBUG] mapper.StudentMapper.delete - <== Updates: 1
testFindAll()的结果
2019-02-16 22:47:38 [DEBUG] mapper.StudentMapper.findAll - ==> Preparing: select sid,sname name,birthday,sex from student
2019-02-16 22:47:38 [DEBUG] mapper.StudentMapper.findAll - ==> Parameters:
2019-02-16 22:47:39 [DEBUG] mapper.StudentMapper.findAll - <== Total: 9
Student{sid=1001, sname='张三', birthday=Wed Oct 10 00:00:00 CST 1990, sex='男'}
Student{sid=1002, sname='李四', birthday=Sat Oct 10 00:00:00 CST 1981, sex='男'}
Student{sid=1003, sname='王五', birthday=Tue Nov 10 00:00:00 CST 1981, sex='女'}
Student{sid=1004, sname='赵六', birthday=Mon Oct 10 00:00:00 CST 1988, sex='男'}
Student{sid=1005, sname='孙七', birthday=Tue Jan 10 00:00:00 CST 1989, sex='女'}
Student{sid=1006, sname='周八', birthday=Wed Oct 10 00:00:00 CST 1990, sex='男'}
Student{sid=1007, sname='张三', birthday=Sun Jun 10 00:00:00 CDT 1990, sex='女'}
Student{sid=1008, sname='张丽', birthday=Fri Dec 11 00:00:00 CST 1998, sex='女'}
Student{sid=1009, sname='李五', birthday=Sat Feb 16 22:41:14 CST 2019, sex='男'}
testFindById()的结果
2019-02-16 22:48:32 [DEBUG] mapper.StudentMapper.findById - ==> Preparing: select sid,sname name,birthday,sex from student where sid = ?
2019-02-16 22:48:32 [DEBUG] mapper.StudentMapper.findById - ==> Parameters: 1001(Integer)
2019-02-16 22:48:33 [DEBUG] mapper.StudentMapper.findById - <== Total: 1
Student{sid=1001, sname='张三', birthday=Wed Oct 10 00:00:00 CST 1990, sex='男'}
testUpdate()的结果
2019-02-16 22:49:18 [DEBUG] mapper.StudentMapper.update - ==> Preparing: update student SET sex=? where sid=?
2019-02-16 22:49:18 [DEBUG] mapper.StudentMapper.update - ==> Parameters: 男(String), 1001(Integer)
2019-02-16 22:49:18 [DEBUG] mapper.StudentMapper.update - <== Updates: 1
testFindByPage()的结果
2019-02-16 22:50:00 [DEBUG] mapper.StudentMapper.findByPage - ==> Preparing: select sid,sname name,birthday,sex from student limit ?, ?
2019-02-16 22:50:01 [DEBUG] mapper.StudentMapper.findByPage - ==> Parameters: 0(Integer), 5(Integer)
2019-02-16 22:50:01 [DEBUG] mapper.StudentMapper.findByPage - <== Total: 5
Student{sid=1001, sname='张三', birthday=Wed Oct 10 00:00:00 CST 1990, sex='男'}
Student{sid=1002, sname='李四', birthday=Sat Oct 10 00:00:00 CST 1981, sex='男'}
Student{sid=1003, sname='王五', birthday=Tue Nov 10 00:00:00 CST 1981, sex='女'}
Student{sid=1004, sname='赵六', birthday=Mon Oct 10 00:00:00 CST 1988, sex='男'}
Student{sid=1005, sname='孙七', birthday=Tue Jan 10 00:00:00 CST 1989, sex='女'}
每个结果最上面都会生成sql语句,#{}代替的地方会出现?,因为它底层用到prepareStatement,并且显示出一些信息,比如:有几条结果 Total、参数 Paramteters、更新了几条 update ,这是配置logback才会出现的,这样就容易观察到SQL语句是否错误,当然也是要添加依赖的,具体步骤上面已经说过了。
2、接口标签增删改
存储学生信息的代码
import java.util.Date;
public class Student {
private int sid;
private String sname;
private Date birthday;
private String sex;
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
'}';
}
}
标签增删改接口
import domain.Student;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface StudentMapper {
@Insert("insert into student(sid,sname,birthday,sex) values(null,#{sname}, #{birthday}, #{sex})")
@Options(useGeneratedKeys = true, keyProperty = "sid")
void insert(Student student);
@Update("update student set sname=#{sname}, birthday=#{birthday}, sex=#{sex} where sid=#{sid}")
void update(Student student);
@Delete("delete from student where sid=#{sid}")
void delete(int sid);
@Select("select * from student")
List<Student> findAll();
@Select("select * from student where sid = #{sid}")
Student findById(int sid);
@Select("select * from student limit #{m}, #{n}")
List<Student> findByPage(Map map);
// 同时根据姓名和性别查询
@Select("select * from student where sname=#{a} and sex=#{b}")
List<Student> find1(@Param("a") String sname, @Param("b") String sex);
/*
方法不能重载,下面这语句会出错
@Select("select * from student where sname=#{a}")
List<Student> find1(String sname);*/
@Select("select * from student where sname=#{a} and sex=#{b}")
List<Student> find2(Map<String, Object> map);
void deleteByIds(List<Integer> ids);
}
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">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- 配置了 mybatis 连接数据库的信息 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8
& ==> &
-->
<property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=false&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 指定映射接口的 包名.类名 ,也就是接口的位置 接口是使用class-->
<mapper class="mapper.StudentMapper"></mapper>
</mappers>
</configuration>
测试类
import domain.Student;
import mapper.StudentMapper;
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.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class TestStudentMapper {
static SqlSessionFactory factory;
static {
// 读取配置文件
try(InputStream in = Resources.getResourceAsStream("mybatis-config.xml")){
// 创建 sqlSession 工厂类
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testInsert() throws ParseException {
SqlSession sqlSession = factory.openSession();
// 先通过 sqlSession 获得接口对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student stu = new Student();
stu.setSname("啊啊啊");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
stu.setBirthday(sdf.parse("1999-5-5"));
stu.setSex("男");
// 通过接口对象中声明的方法 执行sql语句
mapper.insert(stu);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testFindAll() {
SqlSession sqlSession = factory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.findAll();
for (Student student : list) {
System.out.println(student);
}
sqlSession.close();
}
@Test
public void testFindByPage() {
SqlSession sqlSession = factory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("m", 0);
map.put("n", 5);
List<Student> list = mapper.findByPage(map);
System.out.println("mapper 实际类型是:" + mapper.getClass());
for (Student student : list) {
System.out.println(student);
}
sqlSession.close();
}
@Test
public void testFind2() {
SqlSession sqlSession = factory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("a", "张三");
map.put("b", "男");
List<Student> list = mapper.find2(map);
for (Student student : list) {
System.out.println(student);
}
}
@Test
public void testFind1() {
SqlSession sqlSession = factory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.find1("张三", "男");
for (Student student : list) {
System.out.println(student);
}
}
@Test
public void testDeleteByIds() {
SqlSession sqlSession = factory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.deleteByIds(Arrays.asList(1013));
sqlSession.commit();
sqlSession.close();
}
}
和上面代码相同的结果都是一样的,其他的结过也差不多,注解方法和xml方法都可以使代码简洁,不会像以前那样需要些很多那些代码,在代码量上简洁不少,并且mybatis-config.xml文件中接口用的是class、xml文件用的是resource。
总结
mybatis的作用还有很多,需要继续学习,这个是 http://www.mybatis.org/mybatis-3/zh/index.html 官方文档地址(偏向基础),想要了解原理要阅读源码,在应用上更近一步学习 mybatis-plus (https://mp.baomidou.com/ ) 是mybatis的扩展 ,这个持久层框架配置有点多,一不下心容易出错,因此要学会如何找出错误处 ,出错时会有提示哪里出错,要多多关注,错多了,也就知道哪里出错了,可以说是错处了手感,哈哈哈哈。