MyBatis | Hibernate |
是一个SQL语句映射的框架(工具) | 主流的ORM框架、提供了从POJO到数据库表的全套映射机制 |
注重POJO/Map与SQL之间的映射关系。不会为程序员在运行期自动生成SQL | 会自动生成全套SQL语句。 |
自动化程度低、手工映射SQL,灵活程度高 | 因为自动化程度高、映射配置复杂,api也相对复杂,灵活性低. |
需要开发人员熟炼掌据SQL语句 | 开发人同不必关注SQL底层语句开发 |
configuration.xml是系统的核心配置文件,包含数据源和事务管理器等设置和属性信息,XML文档结构如下:
configuration 配置properties 可以配置在 Java 属性配置文件中settings 修改 MyBatis 在运行时的行为方式,如是否需要缓存typeAliases 为 Java 类型命名一个短的名字typeHandlers 类型处理器-系统默认已经为所有类型设置 OKobjectFactory 对象工厂 – 创建 Bean 。plugins 插件 - 拦截 CRUD 操作。environments 环境 -配置数据源environment 环境变量transactionManager 事务管理器 JDBC|JNDI|JTAdataSource 数据源mappers l 映射器 XML 文件| Mapper 类 | 网络资源
配置环境
……
MyBatis有两种事务管理类型:
l JDBC- 这个类型直接全部使用 JDBC 的提交和回滚功能。它依靠使用连接的数据源来管理事务的作用域。l MANAGED- 这个类型什么不做, 它从不提交 、 回滚和关闭连接 。 而是让窗口来管理事务的全部生命周期 。(比如说 Spring 或者 JAVAEE 服务器)
数据源类型有三种:UNPOOLED,POOLED,JNDI。
UNPOOLED- 这个数据源实现只是在每次请求的时候简单的打开和关闭一个连接。虽然这有点慢,但作为一些不需要性能和立即响应的简单应用来说, 不失为一种好选择 。
POOLED- 这个数据源缓存 JDBC 连接对象用于避免每次都要连接和生成连接实例而需要的验证时间。对于并发 WEB 应用,这种方式非常流行因为它有最快的响应时间。
JNDI- 这个数据源实现是为了准备和 Spring 或应用服务一起使用,可以在外部也可以在内部配置这个数据源,然后在 JNDI 上下文中引用它。这个数据源配置只需要两上属性:
1.使用相对路径
cache- 配置给定命名空间的缓存。
cache-ref– 从其他命名空间引用缓存配置。
resultMap – 最复杂,也是最有力量的元素,用来描述如何从数据库结果集中来加载对象。 l
sql – 可以重用的 SQL 块,也可以被其他语句引用。
insert– 映射插入语句
update– 映射更新语句
delete– 映射删除语句
select– 映射查询语句
package cn.hncu.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class SqlSessionUtil {
private static SqlSessionFactory ssf;// 底层对应的是Connection连接池
static {
InputStream in;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");// 从classpath位置加载文件,可以使用相对路径如:cn/hncu/mybatis-config.xml
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
ssf=ssfb.build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSsf() {
return ssf;
}
public static SqlSession getSqlSession() {
return ssf.openSession();
}
public static void main(String[] args) throws SQLException {
//SqlSession是Connection的包装类
//每次获取的是不同的SqlSession对象,如果获取一个session对象且从中访问con,则就会到池中去拿一个连接。由于我们在配置文件中设置池大小只有5,因此只有前5个session对象能够顺利取出con对象,第6个session在获取con时会阻塞。
//一个session对象在获取con之后如果不使用,那么超过默认时间,它所持有的con连接会被收回池中。这样其它session对象又可以从池中获取
System.out.println("ssf:"+ssf);
//下面这段代码,在5个session获取连接之后会阻塞。过一段时间后,才继续执行6-10个session对象
for(int i=1;i<=10;i++){
SqlSession ss=getSqlSession();
System.out.println(i+":sqlSession:"+ss);//sqlSession不同
Connection con=ss.getConnection();
System.out.println(i+":con:"+con);//Connection相同
ss.close();//session对象关闭之后,它所持有的con会还回池中,被下一个session重新持有
}
}
}
intinsert(String statement, Object parameter)
intupdate(String statement, Object parameter)
intdelete(String statement, Object parameter)
List selectList(String statement, Object parameter)
package cn.hncu.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class SqlSessionUtil {
private static SqlSessionFactory ssf;// 底层对应的是Connection连接池
static {
InputStream in;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");// 从classpath位置加载文件,可以使用相对路径如:cn/hncu/mybatis-config.xml
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
ssf=ssfb.build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSsf() {
return ssf;
}
public static SqlSession getSqlSession() {
return ssf.openSession();
}
public static void main(String[] args) throws SQLException {
//SqlSession是Connection的包装类
//每次获取的是不同的SqlSession对象,如果获取一个session对象且从中访问con,则就会到池中去拿一个连接。由于我们在配置文件中设置池大小只有5,因此只有前5个session对象能够顺利取出con对象,第6个session在获取con时会阻塞。
//一个session对象在获取con之后如果不使用,那么超过默认时间,它所持有的con连接会被收回池中。这样其它session对象又可以从池中获取
System.out.println("ssf:"+ssf);
//下面这段代码,在5个session获取连接之后会阻塞。过一段时间后,才继续执行6-10个session对象
for(int i=1;i<=10;i++){
SqlSession ss=getSqlSession();
System.out.println(i+":sqlSession:"+ss);//sqlSession不同
Connection con=ss.getConnection();
System.out.println(i+":con:"+con);//Connection相同
ss.close();//session对象关闭之后,它所持有的con会还回池中,被下一个session重新持有
}
}
}
create database if not exists mybatis default character set 'utf8';
use mybatis;
create table users(
id varchar(32) primary key,
name varchar(32),
pwd varchar(32)
);
insert into users value('U001','Jack','1234');
insert into users value('U010','张三','4321');
CREATE TABLE cpu(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(30)
);
CREATE TABLE persons(
pid VARCHAR(32) PRIMARY KEY,
pname VARCHAR(30)
);
CREATE TABLE cars(
id VARCHAR(32) PRIMARY KEY,
NAME VARCHAR(30),
price NUMERIC(10,2),
pid VARCHAR(32),
CONSTRAINT car_fk FOREIGN KEY(pid) REFERENCES persons(pid)
);
INSERT INTO persons VALUES('P001','Jack');
INSERT INTO persons VALUES('P002','Rose');
INSERT INTO persons VALUES('P003','张三');
INSERT INTO cars VALUES('C001','BMW',100,'P001');
INSERT INTO cars VALUES('C002','BenZ',80,'P001');
INSERT INTO cars VALUES('C003','Jeep',50,'P003');
CREATE TABLE card(
card_id VARCHAR(32) PRIMARY KEY,
card_gov VARCHAR(30),
pid VARCHAR(32) unique,
CONSTRAINT card_fk FOREIGN KEY(pid) REFERENCES persons(pid)
);
INSERT INTO card VALUES('C001','北京市公安局','P001');
INSERT INTO card VALUES('C002','长沙市公安局','P002');
CREATE TABLE roles(
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(30),
);
CREATE TABLE roleuser(
roleid VARCHAR(32),
userid VARCHAR(32),
CONSTRAINT ru_pk PRIMARY KEY(roleid,userid),
CONSTRAINT ru_fk1 FOREIGN KEY(roleid)
REFERENCES roles(id),
CONSTRAINT ru_fk1 FOREIGN KEY(userid)
REFERENCES users(id)
);
INSERT INTO roles VALUES('R001','教师');
INSERT INTO roles VALUES('R002','老板');
INSERT INTO roles VALUES('R003','学生');
INSERT INTO roleuser VALUES('R001','U001');
INSERT INTO roleuser VALUES('R002','U001');
INSERT INTO roleuser VALUES('R003','U001');
INSERT INTO roleuser VALUES('R001','U010');
INSERT INTO roleuser VALUES('R001','U002');
INSERT INTO roleuser VALUES('R002','U003');
INSERT INTO roleuser VALUES('R002','U003');
1)查询学生信息
映射文件
测试代码:2)查询学生信息(接口方式---推荐方式)@Test//查询学生信息 public void query1(){ SqlSession s=SqlSessionUtil.getSqlSession(); List
list=s.selectList("query1"); System.out.println(list); s.close();//执行之后,最好养成关session的习惯 }
映射文件
接口代码
package cn.hncu.domain; import java.util.List; public interface UserMapper { public List
query1(); public List queryByID(String string); }
测试代码@Test//采用接口的方式访问----mybatis推荐的访问方式 public void queryByInterface(){ SqlSession s=SqlSessionUtil.getSqlSession(); //与上一版本不同之处---通过接口访问 UserMapper up=s.getMapper(UserMapper.class); List
list=up.query1(); System.out.println(list); s.close(); }
3)查询学生信息(带单个条件)
映射文件
测试代码
@Test//带条件的查询1:单个条件 public void queryByID(){ SqlSession s=SqlSessionUtil.getSqlSession(); //与上一版本不同之处---通过接口访问 List
list=s.selectList("queryByID", "U001"); System.out.println(list); s.close(); }
4)带多条件的查询2:多个条件
映射文件
测试代码
@Test//带多条件的查询2:多个条件 public void queryUser(){ SqlSession s=SqlSessionUtil.getSqlSession(); User user=new User(); user.setId("U001"); user.setName("Jack"); List
list=s.selectList("queryUser",user); System.out.println(list); }
5)返回结果封装成List
1).增:
映射文件
@Test//insert(插入) public void insert(){ SqlSession s=SqlSessionUtil.getSqlSession(); User user=new User(); user.setId("U005"); user.setName("BGW"); user.setPwd("55555"); s.insert("insert",user); s.commit();//必须显示提交。因为mybatis默认是带事务的即不自动提交 s.close(); }
测试代码
insert into users(id,name,pwd) values(#{id},#{name},#{pwd})
2)单
映射文件
测试代码
delete from users where 1=1 and id=#{id} and name=#{name} @Test//delete删除 public void delete(){ SqlSession s=SqlSessionUtil.getSqlSession(); Map
map = new HashMap (); map.put("id", "U005"); map.put("name", "Jack"); s.delete("delete", map); s.commit();//必须显示提交。因为mybatis默认是带事务的即不自动提交 s.close(); }
3)更新(修改)
映射文件
update users set id=#{id} , name=#{name} ,pwd=#{pwd} where id=#{id}
测试代码@Test//update更新 public void update(){ SqlSession s=SqlSessionUtil.getSqlSession(); Map
map = new HashMap (); User user=new User(); user.setId("U005"); user.setName("大哥王"); s.update("update",user); s.commit();//必须显示提交。因为mybatis默认是带事务的即不自动提交 s.close(); }
4)用mybatis调用底层jdbc实现自动生成主键
映射文件
测试代码
insert into cpu(name) values(#{name})
//mybatis中实现自动生成主键 @Test//底层jdbc的方式 public void generateKey1() throws SQLException{ SqlSession s=SqlSessionUtil.getSqlSession(); Connection con=s.getConnection(); PreparedStatement pst=con.prepareStatement("insert into cpu(name) values(?)",Statement.RETURN_GENERATED_KEYS); pst.setString(1, "ibm"); int a=pst.executeUpdate(); ResultSet rs=pst.getGeneratedKeys(); rs.next(); s.commit(); s.close(); System.out.println(rs.getInt(1)); } @Test//mybatis的方式 public void generateKey2() throws SQLException{ SqlSession s=SqlSessionUtil.getSqlSession(); Map
map=new HashMap (); map.put("name", "intel"); int n=s.insert("generateKey2",map); System.out.println(n); //n是影响的行数 System.out.println(map.get("id")); s.commit(); s.close(); }
1)多条件查询
映射文件
2)where标签的使用
映射文件
3)trim标签的使用
映射文件
4)set标签的使用
映射文件
update users where id=#{id} name=#{name} pwd=#{pwd}
5)foreach标签的使用
映射文件
测试代码
package cn.hncu.demo; import java.util.ArrayList; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import cn.hncu.domain.User; import cn.hncu.utils.SqlSessionUtil; public class Demo2 { @Test public void query(){ SqlSession s = SqlSessionUtil.getSqlSession(); User u = new User(); // u.setId("U001"); u.setName("%Jack%"); // u.setPwd("%1%"); // List
list=s.selectList("user.dyncUser1", u); // List list=s.selectList("user.dyncUser2", u); List list=s.selectList("user.dyncUser3", u); System.out.println(list); } @Test public void update(){ SqlSession s = SqlSessionUtil.getSqlSession(); User u = new User(); u.setId("U005"); u.setName("Tom"); // u.setPwd("1565"); s.update("dyncUpdate",u); s.commit(); s.close(); } @Test public void queryForeach(){ SqlSession s = SqlSessionUtil.getSqlSession(); List
1)一对多查询操作
法1映射文件(内连接)法2映射文件(左外连接)
测试文件
//内联: 查询哪些人有哪些车 @Test public void query(){ SqlSession s = SqlSessionUtil.getSqlSession(); // List
persons = s.selectList("person.person1"); List persons = s.selectList("person.person2"); System.out.println(persons); s.close(); }
2.一对一的多表查询
映射文件测试文件
3.多对多的多表查询//////// 一对一 //////////// @Test public void queryCard(){ SqlSession s = SqlSessionUtil.getSqlSession(); List
cards = s.selectList("person.card"); System.out.println(cards); s.close(); }
映射文件
测试代码///////// 多对多 //////////// @Test public void mutilSelect(){ SqlSession s = SqlSessionUtil.getSqlSession(); List
user = s.selectList("role.mutilSelect"); System.out.println(user); s.close(); }
1)sql代码片段(include标签的使用)
id,name,pwd
2)缓存机制:mybatis和hibernate一样,有两级缓存,第一级是session级别的缓存,第二级是二级缓存
一级缓存演示:
测试代码:
结果:两个r的hashCode一样是session级别的缓存@Test //mybatis和hibernate一样,一级缓存是一定存在的,即同一个session对象同享同一个缓存,因此多次获取相同的对象(内存地址相同即是同一个对象) public void cacheDemo(){ SqlSession s = SqlSessionUtil.getSqlSession(); Role r1=s.selectOne("one", "R001"); System.out.println(r1.hashCode()); Role r2=s.selectOne("one", "R001"); System.out.println(r2.hashCode()); s.close(); }
二级缓存演示:
映射设置
- 在mybatis-config.xml里设置
- 在相应的映射文件设置
- 值对象实现Serializable接口
测试:
@Test //mybatis和hibernate一样,二级缓存需要配置,即两个session对象都从同一个二级缓存中把同一个对象clone到本地一级缓存,因此两个session获取的相同对象的内存地址仍不同,但可以观察到访问数据库只有一次! public void cacheDemo2(){ SqlSession s1 = SqlSessionUtil.getSqlSession(); Role r1=s1.selectOne("one", "R001"); System.out.println("r1"+r1.hashCode()); s1.close();//两个session写在一起同时执行,要先把第一个执行完(close),第二级才能从二级缓存中读取出来有,我的理解为有事务隔离 SqlSession s2 = SqlSessionUtil.getSqlSession(); Role r2=s2.selectOne("one", "R001"); System.out.println("r2"+r2.hashCode()); s2.close(); }
由于事务的隔离性质,要把第一个SqlSession提交后才可以进行第二个输出