Mybatis
概述
1.mybatis是什么?有什么特点?
它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL,延迟加载和缓存等特性,但它的数据库无关性较低。
2.为什么mybatis是半自动的ORM框架?
用mybatis进行开发,需要手动编写SQL语句。而全自动的ORM框架,如hibernate,则不需要编写SQL语句。用hibernate开发,只需要定义好ORM映射关系,就可以直接进行CRUD操作了。由于mybatis需要手写SQL语句,所以它有较高的灵活性,可以根据需要,自由地对SQL进行定制,也因为要手写SQL,当要切换数据库时,SQL语句可能就要重写,因为不同的数据库有不同的方言(Dialect),所以mybatis的数据库无关性低。虽然mybatis需要手写SQL,但相比JDBC,它提供了输入映射和输出映射,可以很方便地进行SQL参数设置,以及结果集封装。并且还提供了关联查询和动态SQL等功能,极大地提升了开发的效率。并且它的学习成本也比hibernate低很多。
配置Mybatis
1.下载需要的jar文件,导入jar包,log4j-1.2.14.jar、mybatis-3.2.2.jar、mysql-connector-java-8.0.13.jar
2.编写mybatis核心配置文件(mybatis-config.xml)和日志配置文件(log4j.properties)
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">
<!--!DOCTYPE configuration SYSTEM "mybatis-3-config.dtd" -->
<configuration>
</configuration>
log4j.properties
log4j.rootLogger = INFO,CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%-4r %-5p [%t] %37c %3x - %m%n
log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] - %m%n
#myApp.root param from web.xml
log4j.appender.FILE.File=log4j.log
log4j.logger.org.apache.ibatis=DEBUG
log4j.logger.com.dao=TRACE
在mybatis-config.xml的configuration标签中插入以下内容此为配置 mysql数据库
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/personmanage?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
3.创建实体类(POJO)
POJO类可以理解为符合JavaBean规范的实体类,它不需要继承和实现任何特殊的Java基类或者接口JavaBean对象的状态保存在属性中,访问必须需要对应的getter和setter方法。
第一步创建com.bean包
第二步创建实体类User.java
package com.bean;
import java.util.Date;
public class Users {
private int id;
private String realname;
private String nickname;
private String pwd;
private String phone;
private String email;
private String address;
private Date createTime;
private int type;
private String realid;
/// setter/getter
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRealname() {
return realname;
}
public void setRealname(String realname) {
this.realname = realname;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getRealid() {
return realid;
}
public void setRealid(String realid) {
this.realid = realid;
}
}
4.DAO层-SQL映射文件(mapper.xml)
创建Dao层,创建UserMapper接口
package com.dao;
import com.bean.Users;
import java.util.List;
public interface UsersMapper {
Users findById(int id);
List<Users> findAll(); // alt+enter
void add(Users user);
void update(Users user);
void delete(Users user);
}
接下来创建SQL映射文件,该文件也是一个XML文件,名为UserMapper.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.dao.UsersMapper">
<select id="findById" parameterType="int"
resultType="com.bean.Users">
select
id,nickname,realname,pwd,phone,email,address,create_time createTime,type,realid
from n_users where id = #{id}
</select>
<select id="findAll" resultType="com.bean.Users">
select
id,nickname,realname,pwd,phone,email,address,create_time createTime,type,realid
from n_users order by id
</select>
<insert id="add" parameterType="com.bean.Users">
insert into n_users
(nickname,realname,pwd,phone,email,address,create_time,type,realid)
values
(#{nickname},#{realname},#{pwd},#{phone},#{email},#{address},#{createTime},#{type},#{realid})
</insert>
</mapper>
mapper:映射文件的根元素节点,只有一个属性namespace。
namespace属性,用于区分不同的mapper,全局唯一。
select:表示查询语句,是MyBatis最常用的元素之一。
id属性:该命名空间下的唯一标识符。
resultType属性:表示SQL语句返回值类型。
parameterType属性:表示SQL语句参数值类型。
在MyBatis中,的占位符是#{}。
ps:1.在插入语句中,我们直接用的实体类的属性,但是这些属性在类中是私有的,无法直接拿到数据,Mybatis会先把首字母大写,再在后面加上括号,调用get方法,获得数据。比如:nickname->Nickname->getNickname();
2.我们使用findAll()方法,我们的返回值是List,但是映射文件中还是User类,这是MyBatis的特性,不关注集合的类型,只关注元素的类型。
3.在findById中,create_time加了个别名,是因为数据库中的属性名和实体类中的属性名不同,MyBatis不能自动将create_time的值赋值到createTime中,加上别名就可以了。
4.因为mybatis默认不是自动提交事务的, 所以其实没有修改数据库, 刚刚新增完后立即返回的结果,是从mybatis为了提高性能设置的缓存里读取的,不是从数据库读取的,所以,上面的修改和添加操作一定要对事务进行提交。
最后在MyBatis(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"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/news?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/dao/UsersMapper.xml"/>
</mappers>
</configuration>
configuration:配置文件根元素节点。
environments:表示配置MyBatis多套运行环境,将SQL映射到多个不同的数据库上,该元素节点下可以配置多个environment子元素节点,但必须指定其中一个默认运行环境(通过default指定)。
environment:配置Mybatis的一套运行环境,需指定运行环境ID,事务管理器,数据源配置等相关信息。
mappers:作用是告诉Mybatis去哪找映射文件(该内容是开发者定义的映射SQL语句),整个项目中可以有一个或多个SQL映射文件。
mapper:mappers的子元素节点,具体指定SQL映射文件的路径,其中resource属性的值表述了SQL映射文件的路径。
5.编写测试类
通过这两篇文章
掌握MyBatis的核心对象
单元测试,Junit
编写出如下代码
package com.dao;
import com.bean.Users;
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.Assert;
import org.junit.Test;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.List;
public class UsersMapperTest {
static final String CONF_FILE = "mybatis-config.xml";
static SqlSessionFactory factory = null;
// 非静态成员,实例成员,成员变量,通过构造方法初始化的
static{
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
try {
InputStream resourceAsStream =
Resources.getResourceAsStream(CONF_FILE);
factory = builder.build(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void findById(){
SqlSession session = factory.openSession(); //
Users user = session.selectOne("com.dao.UsersMapper.findById",1);
session.close();
// 断言=>true就是断言成功,false就是断言失败
//assert(user!=null);
// 断言失败,后续代码不会执行了
Assert.assertNotNull(user);
Assert.assertEquals("管理员",user.getNickname());
Assert.assertEquals("123",user.getPwd());
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = df.format(user.getCreateTime());
Assert.assertEquals("2019-10-09",dateStr);
}
}
重复以上步骤对程序功能进行完善,在UserMapper.xml添加相应的映射,在userMapperTest中添加测试,如下
package com.dao;
import com.bean.Users;
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.Assert;
import org.junit.Test;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.List;
public class UsersMapperTest {
static final String CONF_FILE = "mybatis-config.xml";
static SqlSessionFactory factory = null;
// 非静态成员,实例成员,成员变量,通过构造方法初始化的
static{
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
try {
InputStream resourceAsStream =
Resources.getResourceAsStream(CONF_FILE);
factory = builder.build(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void findById(){
SqlSession session = factory.openSession(); //
Users user = session.selectOne("com.dao.UsersMapper.findById",1);
session.close();
// 断言=>true就是断言成功,false就是断言失败
//assert(user!=null);
// 断言失败,后续代码不会执行了
Assert.assertNotNull(user);
Assert.assertEquals("管理员",user.getNickname());
Assert.assertEquals("123",user.getPwd());
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = df.format(user.getCreateTime());
Assert.assertEquals("2019-10-09",dateStr);
}
@Test
public void findAll(){
SqlSession session = factory.openSession();
List<Users> list= session.selectList("com.dao.UsersMapper.findAll");
session.close();
Assert.assertNotNull(list);
Assert.assertEquals(3,list.size());
Assert.assertEquals("管理员",list.get(0).getNickname()); // Ok?
}
@Test
public void add(){
Users user = new Users();
user.setNickname("lisi");
user.setRealname("李四");
user.setPhone("1111111");
user.setEmail("1111111@11.com");
user.setAddress("洛阳");
user.setPwd("123");
user.setCreateTime(new Date()); // jsp->Form->user
SqlSession session = factory.openSession();
session.insert("com.dao.UsersMapper.add",user);
session.commit(); // 提交事务
session.close();
session = factory.openSession();
Users dbUser = session.selectOne("com.dao.UsersMapper.findById",20);
session.close();
Assert.assertNotNull(dbUser);
Assert.assertEquals("李四",dbUser.getRealname());
}
@Test
public void update() throws ParseException {
// 实际编写DAO层代码的时候,没有这么麻烦
SqlSession session = factory.openSession();
Users user = session.selectOne("com.dao.UsersMapper.findById",20);
user.setNickname("李四");
// 如果修改时间呢 2020-03-03=>2019-10-01 0:0:0,1000 推荐方法如下:
/*Calendar instance = Calendar.getInstance(); // 当前时间
instance.set(Calendar.YEAR,2019); // 设置年份
instance.set(Calendar.MONTH,9); // 月份从0开始,0-11
instance.set(Calendar.DAY_OF_MONTH,1);
instance.set(Calendar.HOUR_OF_DAY,0);
instance.set(Calendar.MINUTE,0);
instance.set(Calendar.SECOND,0);
instance.set(Calendar.MILLISECOND,0);
Date date = instance.getTime();*/
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = df.parse("2019-10-01 02:02:02"); // 页面的表单提交
user.setCreateTime(date);
session.update("com.dao.UsersMapper.update",user);
session.commit(); // 事务提交
Users dbUser = session.selectOne("com.dao.UsersMapper.findById",20);
Assert.assertNotNull(dbUser);
Assert.assertEquals("李四",dbUser.getNickname());
Assert.assertEquals(date,dbUser.getCreateTime());
session.close();
}
@Test
public void delete(){
// 主从表问题?也叫父子表,主键所在的表为主表/父表,外键所在的表为从表/子表
// 1、添加数据时,先加主表,再加从表
// 2、删除数据时,先删除从表,在删主表 ,20,
SqlSession session = factory.openSession();
Users dbUser = session.selectOne("com.dao.UsersMapper.findById",20);
Assert.assertNotNull(dbUser);
session.delete("com.dao.NewsMapper.deleteByUsersId",20);
session.delete("com.dao.AccessLogsMapper.deleteByUsersId",20);
session.delete("com.dao.ReplysMapper.deleteByUsersId",20);
session.delete("com.dao.ShortReplysMapper.deleteByUsersId",20);
session.delete("com.dao.UsersMapper.delete",20);
session.commit(); // 事务
Users user = session.selectOne("com.dao.UsersMapper.findById",20);
Assert.assertNull(user);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper SYSTEM "http://mybatis.org/dtd/mybatis-3-mapper.dtd" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN">
<mapper namespace="com.dao.UsersMapper">
<select resultType="com.bean.Users" parameterType="int" id="findById">
select
id,nickname,realname,pwd,phone,email,address,create_time createTime,type,realid
from n_users
where id = #{id}
</select>
<select resultType="com.bean.Users" parameterType="string" id="findByAccount">
select
id,nickname,realname,pwd,phone,email,address,create_time createTime,type,realid
from n_users
where nickname = #{nickname}
</select>
<select resultType="com.bean.Users" id="findAll">select
id,nickname,realname,pwd,phone,email,address,create_time createTime,type,realid
from n_users
order by id
</select>
<insert parameterType="com.bean.Users" id="add">
insert into
n_users(nickname,realname,pwd,phone,email,address,create_time,type,realid)
values(#{nickname},#{realname},#{pwd},#{phone},#{email},#{address},#{createTime},#{type},#{realid})
</insert>
<!--不是最终版的,完全修改,不会更新主键-->
<update parameterType="com.bean.Users" id="update">
update n_users
setnickname = #{nickname},realname = #{realname},pwd = #{pwd},phone = #{phone},email = #{email},address = #{address},create_time = #{createTime},type = #{type},realid = #{realid}
where id = #{id}
</update>
<!--很多系统是不允许删除?考虑多表关联问题?-->
<delete parameterType="int" id="delete">
delete from n_users where id = #{id}
</delete>
</mapper>