MyBatis
1.什么是 MyBatis?
MyBatis是对jdbc的轻量级封装,是一个orm对象关系映射框架,(通过xml或注解的方式,把实体类与表之间做映射关系),目的是为了简化开发,达到 以OOP的方式来操作数据库(不写sql)。
dbutils是一个jdbc组件;
mybatis–>mybatis-plus(大大提高开发效率)
mybatis是一个半自动的orm框架,还要写sql.
2.使用mybatis
2.1 安装环境:
mybatis的核心jar包;数据库驱动包
2.2 创建xml文件:
xml配置文件mybatis-config.xml,一般这个文件放src根目录
xml映射文件blog-mapper.xml,这个文件放dao包下;
把映射文件放在配置文件中;
// xml配置文件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>
<!--加载外部properties资源文件,通过${}表达式根据key获取值--> //可没有
<properties resource="jdbc.properties"></properties>
<!--数据库环境配置-->
//就是一个默认的数据库id 一定是下面的数据库的id
//如果是多数据库环境
//SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(res,"dev");
//dev就是对应的数据库环境 不加就是默认的数据库环境
<environments default="development">
<!--单数据库 环境,一个environment对应的SqlSessionFactory-->
<environment id="development">
<!--事务管理机制:JDBC有事务|MANAGED无事务-->
<transactionManager type="JDBC"/>
<!--数据库连接池:UNPOOLED(不使用)|POOLED(使用)|JNDI(已过时)-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--映射文件:统一写sql语句-->
<mappers>
<!--把映射文件包含进来--> //可添加多个
<mapper resource="com/javasm/sys/mapper/myblog.xml"></mapper> //把映射文件放在配置文件中;
</mappers>
</configuration>
//xml映射文件blog-mapper.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="aaa"> //可改
<select id="selectBlog" resultType="com.javasm.sys.enity.TestUser"> //id-方法名字 resultType-返回类型
//sql语句 testuser-表的名字 bid-表里的列名字 #{bid}-实体类对应的属性
select * from testuser where bid = #{bid}
</select>
</mapper>
2.3 从xml文件构建SqlsessionFactory对象
该对象的创建,需要依赖数据源;需要映射文件信息
2.4 获取 SqlSession
2.5 执行操作
public class Test01 {
public static void main(String[] args) {
//1.加载xml配置文件,创建SqlSessionFactory对象
InputStream res = Test01.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//解析mybatis-config中的数据,读取到内存,相当于DataSource
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(res);
//2.获取sqlSession对象,相当于Connection
SqlSession s = ssf.openSession();
//3.执行数据库操作,因为解析xml映射的时候,内部吧namespace.id拼接作为key,
//这里的aaa就是 xml映射文件里的 <mapper namespace="aaa">
Object o = s.selectOne("aaa.selectBlog",1);
System.out.println(o);
//关闭连接
s.close();
}
}
3.核心对象:
SqlSessionFactoryBuilder:
用来加载文件,创建Facotyr,用完即销毁。
SqlSessionFactory:
该对象全局唯一。
openSession()
SqlSession:
执行数据库操作,openSession创建对象,执行操作,操作完成后关闭对象。
Object obj = selectOne("映射文件的namespace.标签id",查询参数)
List<Object> selectList("namesapce.id",查询参数)
T t = getMapper(Class<T> clz)
4.settings标签与日志
<?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="jdbc.properties"></properties>
<settings>
<!--运行日志配置--> //在运行过程中把sql语句打印 //name-表示是在配置运行日志 //value对应运行日志的类型
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启驼峰命名映射--> //支持驼峰映射,把下划线分割的字段映射成实体类驼峰命名
//例如数据库字段名是create_name 实体类是createTime 有了这个配置后就不会报错
<setting name="mapUnderscoreToCamelCase" value="true"></setting>
</settings>
<!--只针对于实体类,进行包扫描配置别名,别名即类名,忽略大小写--> 接口不可配置 类名绝对不能重复
<typeAliases>
<package name="com.javasm"></package> //这个是把com.javasm里的类都扫描自动生成别名了
</typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com\javasm\sys\mapper\blog-mapper.xml"></mapper>
<mapper resource="com\javasm\sys\mapper2\blog-mapper2.xml"></mapper>
</mappers>
</configuration>
内置别名
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
5.mapper映射
<?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="aaa">
/* 返回对象
<!--简单类型类型:8个包装类对象,String,Date-->
<!--public Blog selectBlog(Integer bid)-->
<!--select:表示查询,id:类似于dao接口的方法名,resultType:返回值类型,parameterType:参数类型-->
<!parameterType:参数类型 可不写-->
*/
<select id="selectBlog" parameterType="Integer" resultType="TestUser">
select * from testuser where bid = #{bid}
</select>
/* 返回List
<!--public List<Blog> selectBlogs(Blog b)-->
<!--resultType在mybatis内部表示对数据库表的单行记录封装的类型(实体,Map)-->
<!--#{实体类的成员变量名}-->
*/
<select id="selectList" resultType="TestUser">
select * from testuser where bid = #{bid}
</select>
/* 返回Map
<!--public List<Map<String,Object>> selectBlogs2Map(Blog b)-->
*/
<select id="selectMap" resultType="Map" >
select * from testuser where bname like "%"#{bname}"%"
</select>
</mapper>
public class Test01 {
public static void main(String[] args) {
InputStream res = Test01.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(res);
SqlSession s = ssf.openSession();
//只从数据库取一个对象或数据的时候 调用selectOne()方法
Object o = s.selectOne("aaa.selectBlog",1);
//从数据库取List集合时 调用selectList()
List<Object> o = s.selectList("aaa.selectList",1);
//Map集合时 也是调用selectList()
TestUser b = new TestUser();
b.setBname("a");
List<Map<String,Object>> o = s.selectList("aaa.selectMap", b);
System.out.println(o);
s.close();
}
}
mapper映射文件中的注意点:
- namespace:必须唯一,不同文件不能同名的namesapce
- 查询语句的标签必须是select,id在同一个mapper文件中不能同名,paramterType:表示参数类型;resultType:返回值类型,表示对数据库表单行记录的封装类型。
- 在sql语句中获取参数的语法:#{简单类型的随便写,对象类型的写成员变量名,}
6.SqlSession的getMapper方法
- 映射文件的namespace必须是接口的全名称
- id必须是接口中的方法名
//1.先写接口
public interface BlogMapper {
//传入整数 返回实体类对象
public TestUser selectById(Integer bid);
//传入对象 返回实体类对象
public TestUser selectTestUser(TestUser b);
//传入map集合 返回实体类对象
public TestUser selectTestUserMapParam(Map<String,Object> map);
//内部仍然是把参数封装为map,把@Param注解的值作为key
public TestUser selectTestUserParam(@Param("bid_param")Integer bid, @Param("bname_param") String bname);
//传入注解值 返回List
public List<TestUser> selectTestUsers(@Param("by") String by, @Param("sort") String sort);
//传入整数 返回数据库字段内容
public String selectTitleById(Integer bid);
}
//2. 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:接口的全名称-->
<mapper namespace="com.javasm.sys.mapper.BlogMapper">
<!--id:必须是接口的方法名,-->
<select id="selectById" resultType="TestUser">
<!--#{bid} 这个里面随便写 #{a} #{ccc} #{id} 都可以去得到方法传过来的Integer值 不可以空的#{}-->
select * from testuser where bid = #{bid}
</select>
<!--#{传入对象的成员变量名}-->
<select id="selectTestUser" resultType="TestUser">
select * from testuser where bid = #{bid}
</select>
<!--#{map的key}-->
<select id="selectTestUserMapParam" resultType="TestUser">
select * from testuser where bid = #{bid_key}
</select>
<!--#{@Param注解的值}-->
<select id="selectTestUserParam" resultType="TestUser">
select * from testuser where bid=#{bid_param} and bname=#{bname_param}
</select>
<select id="selectTestUsers" resultType="TestUser">
select * from testuser order by ${by} ${sort}
</select>
<select id="selectTitleById" resultType="string">
select bname from testuser where bid=#{bid}
</select>
</mapper>
//3.运行
public class TestGetMapper {
public static void main(String[] args) {
InputStream in = TestGetMapper.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(in);
SqlSession s = ssf.openSession();
BlogMapper mapper = s.getMapper(BlogMapper.class);
//传入整数 返回实体类对象
TestUser blog = mapper.selectById(1);
//传入对象 返回实体类对象
TestUser t = new TestUser();
t.setBid(1);
TestUser blog = mapper.selectTestUser(t);
// 传入map集合 返回实体类对象
Map<String,Object> map = new HashMap<>();
map.put("bid_key",2);
System.out.println(map);
TestUser blog = mapper.selectTestUserMapParam(map);
// 内部仍然是把参数封装为map,把@Param注解的值作为key
TestUser blog = mapper.selectTestUserParam(3,"bbb");
// 传入注解值 返回List
List<TestUser> blog = mapper.selectTestUsers("bid", "desc");
// 传入整数 返回数据库字段内容
String blog = mapper.selectTitleById(1);
System.out.println(blog);
s.close();
}
}
7.如何多参数传递
5.1 封装实体
5.2 封装map,适合于多参数不属于同一个实体
5.3 加注解@Param,加到mapper接口的方法形参上
例子参考上述例子
//额外知识
//单例模式
public class SSFUtil {
private static SqlSessionFactory ssf;//谁单例谁静态
static{
//给ssf对象实例化
InputStream in = null;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
ssf = new SqlSessionFactoryBuilder().build(in);
}
public static SqlSessionFactory getFactory(){
return ssf;
}
}
//运行
public class TestGetMapper {
public static void main(String[] args) {
SqlSessionFactory factory = SSFUtil.getFactory();
SqlSession s = factory.openSession();
BlogMapper mapper = s.getMapper(BlogMapper.class);
//传入整数 返回实体类对象
TestUser blog = mapper.selectById(1);
System.out.println(blog);
s.close();
}
}
8.跟踪源码:
XMLConfigBuilder:xml配置文件解析对象,构建器build模式,用来构建对象。
Configuration configuration
parse(),返回Configuration配置对象。
XMLMapperBuilder:xml映射文件的解析对象,最重要的是得到mappedStatements集合
parse(),解析mapper.xml,重点在于解析SELECT|INSERT|UPDATE|DELETE四类标签
Configuration:配置对象,把xml配置文件数据读取到该对象
Environment:数据库环境,数据库连接信息
TypeAliasRegistry:类型别名注册,别名信息
Map<String, MappedStatement> mappedStatements:sql映射结果集,key:namespace.id,value:MappedStatement(操作类型,sqlResource)
variables:properties资源配置信息。
DefaultSqlSessionFactory implements SqlSessionFactory
Configuration configuration
MappedStatement:相当于一个select|update|delete|insert标签的解析结果
DefaultSqlSession implements SqlSession
Configuration configuration;
Executor:执行器对象。
用来执行具体的数据库操作
总结:
SqlsessionFactoryBuilder.build()
XMLConfigBuilder与XMLMapperBuilder是为了解析xml配置与xml映射文件,得到Configuration对象
创建出SqlsessionFactory对象,持有Configuration对象对象:new DefaultSqlSessionFactory(Configuration config)
opensession()
得到Executor执行器对象。打开Connection连接
selectOne或selectList的时候:
根据传递进去的String(namespace.id)去Configuration得到一个具体的MappedStatement对象,执行query方法进行数据库查询,结果集封装。
getMapper方法:
该方法传入接口的类对象,返回代理对象。
9.代理模式:proxy
作用:当一个已经存在的对象中的某个方法不满足需求,此时需要代理模式。
静态代理:
创建代理类,必须与被代理对象同类型;代理对象必须持有被代理对象。
动态代理:
代理类不再手工创建,在程序运行期间动态的在jvm内创建的代理类,并编译。
//静态代理
//连接代理
public class ConnectionProxy implements Connection {
private Connection source;
private MyPool pool;
public ConnectionProxy(Connection source,MyPool pool) {
this.source = source;
this.pool= pool;
}
@Override
public Statement createStatement() throws SQLException {
return this.source.createStatement();
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
this.source.setAutoCommit(autoCommit);
}
//这个方法不需要调用原生的jdbc的close方法
//别的省略没写.........太长了
@Override
public void close() throws SQLException {
pool.addConnection(this);//此处的this表示ConnectionProxy对象
System.out.println("放回连接池");
}
}
//连接池
public class MyPool {
//链表,删除插入快,查询慢
private LinkedList<Connection> list = new LinkedList<>();
public void init() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
for(int i=0;i<10;i++){
//1.原生的jdbc的连接对象,被代理对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/three_project", "root", "root");
//2.为原生connection加一层代理对象
Connection proxy = new ConnectionProxy(connection,this);//此处的this时MyPool对象
addConnection(proxy);
}
}
public Connection getConnection(){
System.out.println("获取前,当前大小:"+list.size());
Connection pop = list.pop();
System.out.println("获取后,当前大小:"+list.size());
return pop;
}
public void addConnection(Connection conn){
list.push(conn);
}
}
//运行
public class TestConnection {
public static void main(String[] args) throws Exception {
MyPool pool = new MyPool();
pool.init();
Connection connection = pool.getConnection();
//TODO 数据库操作
PreparedStatement preparedStatement = connection.prepareStatement("select * from blogs");
System.out.println(preparedStatement);
connection.close();//希望close方法不要关闭连接,而是返回list
Connection connection2 = pool.getConnection();
//TODO 数据库操作
connection2.close();
}
}
10.#{}与${}:
写在映射文件中,sql语句获取参数.
#{} sql语句使用?占位符,PreparedStatement.
不
使
用
占
位
符
,
S
t
a
t
e
m
e
n
t
,
平
常
不
用
,
但
是
如
果
参
数
是
表
名
,
字
段
名
等
s
q
l
语
句
的
语
法
部
分
。
必
须
使
用
{} 不使用占位符,Statement,平常不用,但是如果参数是表名,字段名等sql语句的语法部分。必须使用
不使用占位符,Statement,平常不用,但是如果参数是表名,字段名等sql语句的语法部分。必须使用{}
select ${} from blogs where bid=#{} order by ${} ${}
11.公共sql语句块
//id名字随意命名 不过要对应下边的<include refid="allFields"></include>
<sql id="allFields">
uid,uname,uphone,upwd,uemail,create_time,update_time,rid
</sql>
<select id="selectUserById" resultType="Sysuser">
select
<include refid="allFields"></include>
from sysuser where uid = #{uid}
</select>
12.动态sql
动态sql:在xml映射文件中进行sql拼接
if:条件判断,传入boolean表达式,多条件使用and或or
where:生成where关键字,并忽略紧跟其后的and或or,如果where中间的是空的,则不生成where了
set:生成set关键字,并忽略最后一个逗号.
foreach:遍历数组和集合参数,批量删除,批量添加
choose-when-otherwise:单条件查询 只会根据到第一个匹配到的进行查询 不常用
//where:生成where关键字,并忽略紧跟其后的and或or,如果where中间的是空的,则不生成where了
public List<Sysuser> selectUsers(Sysuser suser);//多条件查询
//映射语句
<select id="selectUsers" resultType="Sysuser" parameterType="Sysuser">
select * from sysuser
<where>
<if test="uid!=null">
and uid=#{uid}
</if>
<if test="uname!=null and uname!=''">
and uname=#{uname}
</if>
<if test="uphone!=null and uphone!=''">
and uphone=#{uphone}
</if>
<if test="upwd!=null and upwd!=''">
and upwd=#{upwd}
</if>
<if test="uemail!=null and uemail!=''">
and uemail=#{uemail}
</if>
</where>
order by update_time desc
</select>
// set:生成set关键字,并忽略最后一个逗号.
public int updateUserByIdSelective(Sysuser suser);//选择非空字段更新
//映射语句
<update id="updateUserByIdSelective" parameterType="sysuser">
update sysuser
<set>
<if test="uname!=null">
uname=#{uname},
</if>
<if test="uphone!=null and uphone!=''">
uphone=#{uphone},
</if>
<if test="upwd!=null and upwd!=''">
upwd=#{upwd},
</if>
<if test="uemail!=null and uemail!=''">
uemail=#{uemail},
</if>
</set>
where uid=#{uid}
</update>
//foreach:遍历数组和集合参数,批量删除,批量添加
//批量添加
public int addUsers(Sysuser[] suser);//批量添加用户
//映射语句
<insert id="addUsers">
insert into sysuser(uname,upwd,uphone,uemail,rid) values
<foreach collection="array" separator="," item="suser">
(#{suser.uname},#{suser.upwd},#{suser.uphone},#{suser.uemail},#{suser.rid})
</foreach>
</insert>
//批量删除
public int deleteUsersByIds(@Param("uids") Integer[] uids);
//批量删除,mybatis底层把参数封装为map,默认key为:array,map.put("array",Integer[])
//映射语句
<!--collection:形参为数组写array|形参为list写list (1,2,3)-->
<delete id="deleteUsersByIds">
delete from sysuser where uid in
<foreach collection="uids" open="(" close=")" separator="," item="userid">
#{userid}
</foreach>
</delete>
//choose-when-otherwise:单条件查询 只会根据到第一个匹配到的进行查询
public List<Sysuser> selectUsersOneCondition(Sysuser suser);//单条件查询
//映射语句
<select id="selectUsersOneCondition" resultType="Sysuser" parameterType="Sysuser">
select * from sysuser
<where>
<choose>
<when test="uid!=null">
uid=#{uid}
</when>
<when test="uname!=null and uname!=''">
uname=#{uname}
</when>
<when test="uphone!=null and uphone!=''">
uphone=#{uphone}
</when>
<otherwise></otherwise>
</choose>
</where>
order by update_time desc
</select>
13 insert|update|deleted
这三个标签都没有resultType属性,操作结果需要靠接口的返回值来判断。返回值int受影响行数.
insert
public interface BlogMapper {
public int addUser(Trole tro);
public int addUsers(Trole[] tro);//批量添加用户
public int addUserAndGetUserId(Trole tro);
}
//映射语句
<mapper namespace="com.javasm.sys.mapper.BlogMapper">
//如果role_Name或createTime实体类没有内容那么数据库就会赋值null 别的没有的直接默认值或者null
<insert id="addUser" parameterType="Trole">
insert into trole ( role_Name,createTime ) values ( #{roleName},#{createTime} )
</insert>
//mybatis底层把参数封装为map,默认key为:array,map.put("array",Trole[])
//如果加入注解 key为:注解的值, 假如多参数传入了gid 里面也可以直接取#{gid}传入的值
//foreach就相当于一个循环
// <foreach collection="array" separator="," item="tro">
// (#{gid},#{tro.createTime})
// </foreach>
<insert id="addUsers">
insert into trole ( role_Name,createTime ) values
<foreach collection="array" separator="," item="tro">
(#{tro.roleName},#{tro.createTime})
</foreach>
</insert>
// 如果数据库是主键自增的,在insert标签中useGeneratedKeys="true" keyProperty="形参对象的属性名" 获取自增主键id
//然后通过实体类.getrid获取
<insert id="addUserAndGetUserId" parameterType="Trole" useGeneratedKeys="true" keyProperty="rid" >
insert into trole ( role_Name,createTime ) values ( #{roleName},#{createTime} )
</insert>
</mapper>
//Test
public class TsetInser {
private static SqlSessionFactory factory = null;
private SqlSession s = null;
//最开始的时候走这里一次 ---------- @BeforeClass
@BeforeClass
public static void init(){
factory = SessionFactoryUtil.getFactory();
}
//每次进方法的时候走这里一次 ---------- @Before
@Before
public void oppension(){
s = factory.openSession();
}
@Test
public void test1_addUser(){
BlogMapper mapper = s.getMapper(BlogMapper.class);
Trole user = new Trole();
user.setRoleName("员工si");
int i = mapper.addUser(user);
s.commit();
System.out.println(i);
}
@Test
public void test1_addUsers(){
BlogMapper mapper = s.getMapper(BlogMapper.class);
Trole a = new Trole();
Trole b = new Trole();
Trole c = new Trole();
a.setRoleName("a");
b.setRoleName("b");
c.setRoleName("c");
Trole[] user = new Trole[]{a,b,c};
int i = mapper.addUsers(user);
s.commit();
System.out.println(i);
}
@Test
public void test1_addUserAndGetUserId(){
BlogMapper mapper = s.getMapper(BlogMapper.class);
Trole user = new Trole();
user.setRoleName("员工s");
int i = mapper.addUserAndGetUserId(user);
s.commit();
System.out.println(i);
System.out.println(user.getRid());
}
//每个方法退出后走这里一次 ---------- @After
@After
public void closeSession(){
s.close();
}
//最后的最后走这里一次 ---------- @AfterClass
@AfterClass
public static void destory(){
factory=null;
System.out.println("destory");
}
}
update
public interface BlogMapper {
public int updateUserById(Trole tro);//全字段更新
public int updateUserByIdSelective(Trole tro);//选择非空字段更新
}
//update映射语句
<mapper namespace="com.javasm.sys.mapper.BlogMapper">
//只更新sql包括的内容 没有复制的会把数据库里已有的内容改成null
<update id="updateUserById" parameterType="Trole">
update trole set role_Name=#{roleName},createTime=#{createTime} where r_id=#{rid}
</update>
//通过set if标签实现 选择非空字段更新
<update id="updateUserByIdSelective" parameterType="Trole">
update trole
<set>
<if test="roleName!=null">
role_Name=#{roleName},
</if>
<if test="createTime!=null and createTime!=''">
createTime=#{createTime},
</if>
</set>
where r_id=#{rid}
</update>
</mapper>
public class TestUpdate {
private static SqlSessionFactory factory = null;
private SqlSession s = null;
@BeforeClass
public static void init(){
factory = SessionFactoryUtil.getFactory();
}
@Before
public void oppension(){
s = factory.openSession();
}
@Test
public void test1_upUser(){
BlogMapper mapper = s.getMapper(BlogMapper.class);
Trole user = new Trole();
user.setRoleName("ccccc");
user.setRid(16);
int i = mapper.updateUserById(user);
s.commit();
System.out.println(i);
}
@Test
public void test1_upUser2(){
BlogMapper mapper = s.getMapper(BlogMapper.class);
Trole user = new Trole();
user.setRoleName("ssssss");
user.setRid(14);
int i = mapper.updateUserByIdSelective(user);
s.commit();
System.out.println(i);
}
@After
public void closeSession(){
s.close();
}
@AfterClass
public static void destory(){
factory=null;
System.out.println("destory");
}
}
deleted
public interface BlogMapper {
public int deleteUserById(Integer uid);
//批量删除,mybatis底层把参数封装为map,默认key为:array,map.put("array",Integer[])
public int deleteUsersByIds(@Param("rids") Integer[] rids);
//批量删除mybatis底层把参数封装为map,默认key为:list,map.put("list",Integer[])
public int deleteUsersByIds2(List<Integer> rids);
}
//deleted映射语句
<mapper namespace="com.javasm.sys.mapper.BlogMapper">
<delete id="deleteUserById" parameterType="Integer">
delete from trole where r_id=#{rid}
</delete>
//批量删除,mybatis底层把参数封装为map,默认key为:array,map.put("array",Integer[])
//item可随意输入 只要对应#{ccc}
<delete id="deleteUsersByIds">
delete from trole where r_id in
<foreach collection="rids" open="(" close=")" separator="," item="ccc">
#{ccc}
</foreach>
</delete>
//批量删除mybatis底层把参数封装为map,默认key为:list,map.put("list",Integer[])
//item可随意输入 只要对应#{ccc}
<delete id="deleteUsersByIds2">
delete from trole where r_id in
<foreach collection="list" open="(" close=")" separator="," item="ccc">
#{ccc}
</foreach>
</delete>
</mapper>
public class TestDelete {
private static SqlSessionFactory factory = null;
private SqlSession s = null;
@BeforeClass
public static void init(){
factory = SessionFactoryUtil.getFactory();
}
@Before
public void oppension(){
s = factory.openSession();
}
@Test
public void test1_deleteUserById(){
BlogMapper mapper = s.getMapper(BlogMapper.class);
int i = mapper.deleteUserById(9);
s.commit();
System.out.println(i);
}
@Test
public void test1_deleteUserByIds(){
BlogMapper mapper = s.getMapper(BlogMapper.class);
Integer[] rids = new Integer[]{10,11,12};
int i = mapper.deleteUsersByIds(rids);
s.commit();
System.out.println(i);
}
@Test
public void test1_deleteUserByIds2(){
BlogMapper mapper = s.getMapper(BlogMapper.class);
List<Integer> rids = new ArrayList<>();
rids.add(6);
rids.add(7);
int i = mapper.deleteUsersByIds2(rids);
s.commit();
System.out.println(i);
}
@After
public void closeSession(){
s.close();
}
@AfterClass
public static void destory(){
factory=null;
System.out.println("destory");
}
}
14.resultMap:结果集映射
实体类属性名与数据库的字段名不一致;
//column-数据库字段名 property-实体类属性名
<resultMap id="自定义" type="实体类别名">
<id column="" property="">主键列映射
<result column="" property=""> 非主键列映射e
<mapper namespace="com.javasm.sys.mapper.SysroleMapper">
<!--把某列映射到某个类的某个成员变量-->
<resultMap id="roleResultMap" type="sysrole">
<id column="rid" property="rid"></id><!--主键列映射-->
<result column="role_name" property="rname"></result><!--非主键列映射-->
</resultMap>
<!--resultType:实体对象,Map,String,Integer,Double-->
<!--resultMap:写的时resultMap标签的id-->
<select id="selectRoleByID" resultMap="roleResultMap">
select * from sysrole where rid = #{rid}
</select>
</mapper>
15.collection,association对象关系映射
resultMap的子标签
关联查询:查询用户对象的时候,把用户的角色对象关联查询出来。
关联返回的List用collection 关联返回的对象用association
association: 多对一,一对一关系
查询用户对象的时候,把用户的角色对象关联查询出来。
<mapper namespace="com.javasm.sys.mapper.SysuserMapper">
//同名的可不加映射
<resultMap id="userResultMap" type="Sysuser">
<id column="uid" property="uid"></id>
<result column="uname" property="uname"></result>
<result column="rid" property="rid"></result>
<!--association:column:外键列,javaType:成员变量的类型(类名),select:二次查询的位置-->
// 该实体类 加了关联的那个表的实体类对象
//property-实体类属性名 javaType-属性名类型 column-下个跳转方法需要用到的id select-跳转到哪个方法
<association property="srole" javaType="Sysrole" column="rid" select="com.javasm.sys.mapper.SysroleMapper.selectRoleByID"></association>
</resultMap>
//这个同名也得加映射
<resultMap id="userResultMap2" type="Sysuser">
<id column="uid" property="uid"></id>
<result column="uname" property="uname"></result>
<result column="upwd" property="upwd"></result>
<result column="uphone" property="uphone"></result>
<result column="uemail" property="uemail"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
<result column="rid" property="rid"></result>
<!--association:column:外键列,javaType:成员变量的类型(类名)-->
<association property="srole" javaType="Sysrole">
//增加的那个关联的实体类属性 对应的映射
<id column="rid" property="rid"></id> //主键
<result column="role_name" property="rname"></result> //需关联查询的内容
</association>
</resultMap>
<select id="selectUserAndRoleByUserid" resultMap="userResultMap" parameterType="Integer">
select * from sysuser where uid=#{uid}
</select>
<select id="selectUserAndRoleByUserid2" resultMap="userResultMap2" parameterType="Integer">
select u.*,r.role_name from sysuser u,sysrole r where u.rid=r.rid and u.uid=#{uid}
</select>
</mapper>
//三种方法 一对多关联查询
public class TestAssociaction {
private static SqlSessionFactory factory =null;
private SqlSession s =null;
//junit测试方法
@BeforeClass
public static void init(){
factory = SessionFactoryUtil.getFactory();
}
@Before
public void opensession(){
s = factory.openSession();
}
//查询用户对象的时候,把用户的角色对象关联查询出来。
//手工发起两次查询。
@Test
public void test1_selectUserAndRoleByUserId(){
SysuserMapper um = s.getMapper(SysuserMapper.class);
SysroleMapper rm= s.getMapper(SysroleMapper.class);
Sysuser sysuser = um.selectUserById(1);
//通过查到的对象取到关联的id
Integer rid = sysuser.getRid();
//通过关联的id再次查询
Sysrole sysrole = rm.selectRoleByID(rid);
sysuser.setSrole(sysrole);
System.out.println(sysuser);
}
//mybatis内部发起二次查询,需要通过ResultMap的标签的association关系标签来指定二次查询的位置
@Test
public void test2_selectUserAndRoleByUserId(){
SysuserMapper um = s.getMapper(SysuserMapper.class);
Sysuser sysuser = um.selectUserAndRoleByUserid(1);
System.out.println(sysuser);
}
//sql语句使用表关联查询,一次查询把所有结果查询出来,然后把结果列映射到不同的对象中 (不推荐)
@Test
public void test3_selectUserAndRoleByUserId(){
SysuserMapper um = s.getMapper(SysuserMapper.class);
Sysuser sysuser = um.selectUserAndRoleByUserid2(1);
System.out.println(sysuser);
}
@After
public void closeSession(){
s.close();
}
@AfterClass
public static void destory(){
factory=null;
System.out.println("destory");
}
}
collection:一对多 多对多
查询角色对象的时候,把该角色下的所有用户查询出来。
//实体类属性表
public class Sysrole {
private Integer rid;
private String rname;
private List<Sysuser> userList;//角色对象聚合了用户对象
}
public interface SysroleMapper {
public Sysrole selectRoleByID(Integer rid);//单查询角色对象
public Sysrole selectRoleAndUsersByRID(Integer rid);//查询角色对象以及该角色下的用户集合
public Sysrole selectRoleAndUsersByRID2(Integer rid);//查询角色对象以及该角色下的用户集合
}
<?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.javasm.sys.mapper.SysroleMapper">
<!--把某列映射到某个类的某个成员变量-->
<resultMap id="roleResultMap" type="sysrole">
<id column="rid" property="rid"></id><!--主键列映射-->
<result column="role_name" property="rname"></result><!--非主键列映射-->
</resultMap>
<resultMap id="roleAndUsersResultMap" type="sysrole">
<id column="rid" property="rid"></id><!--主键列映射-->
<result column="role_name" property="rname"></result><!--非主键列映射-->
<!--ofType:集合的泛型类型,-->
<collection property="userList" column="rid" ofType="Sysuser" select="com.javasm.sys.mapper.SysuserMapper.selectUsersByRoleid"></collection>
</resultMap>
<resultMap id="roleAndUsersResultMap2" type="sysrole">
<id column="rid" property="rid"></id><!--主键列映射-->
<result column="role_name" property="rname"></result><!--非主键列映射-->
<!--property:实体类新增的那个集合对象名称-->
<!--ofType:集合的泛型类型,-->
<collection property="userList" ofType="Sysuser">
<id column="uid" property="uid"></id>
<result column="uname" property="uname"></result>
<result column="upwd" property="upwd"></result>
<result column="uphone" property="uphone"></result>
<result column="uemail" property="uemail"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
</collection>
</resultMap>
<!--resultType:实体对象,Map,String,Integer,Double-->
<!--resultMap:写的时resultMap标签的id-->
<select id="selectRoleByID" resultMap="roleResultMap">
select * from sysrole where rid = #{rid}
</select>
<select id="selectRoleAndUsersByRID" parameterType="int" resultMap="roleAndUsersResultMap">
select * from sysrole where rid = #{rid}
</select>
<select id="selectRoleAndUsersByRID2" parameterType="int" resultMap="roleAndUsersResultMap2">
select u.*,r.role_name from sysuser u,sysrole r where u.rid=r.rid and r.rid=#{rid}
</select>
</mapper>
public class TestCollection {
private static SqlSessionFactory factory =null;
private SqlSession s =null;
//junit测试方法
@BeforeClass
public static void init(){
factory = SessionFactoryUtil.getFactory();
}
@Before
public void opensession(){
s = factory.openSession();
}
//建议使用这种,手工调用两个mapper对象中的方法进行单表查询
@Test
public void test1_selectRoleAndUsersByRoleId(){
int rid= 1;
SysroleMapper mapper = s.getMapper(SysroleMapper.class);
SysuserMapper um = s.getMapper(SysuserMapper.class);
Sysrole sysrole = mapper.selectRoleByID(rid);
List<Sysuser> userList = um.selectUsersByRoleid(rid);
sysrole.setUserList(userList);
System.out.println(sysrole);
}
//mybatis的级联查询
@Test
public void test2_selectRoleAndUsersByRoleId(){
int rid= 1;
SysroleMapper mapper = s.getMapper(SysroleMapper.class);
Sysrole sysrole = mapper.selectRoleAndUsersByRID(rid);
System.out.println(sysrole);
}
//基于数据库表的级联查询。 (不推荐)
@Test
public void test3_selectRoleAndUsersByRoleId(){
int rid= 1;
SysroleMapper mapper = s.getMapper(SysroleMapper.class);
Sysrole sysrole = mapper.selectRoleAndUsersByRID2(rid);
System.out.println(sysrole);
}
@After
public void closeSession(){
s.close();
}
@AfterClass
public static void destory(){
factory=null;
System.out.println("destory");
}
}
16.动态代理
/**
* 代理模式:
* 被代理对象:原生已存在的对象,需要扩展的对象
* 代理对象:需要我们创建的新对象,静态代理:手工创建代理类;动态代理:在程序运行期间,jvm内部动态的创建的代理类$Proxy0.java,编译为.class,实例化返回代理对象
*
*
* 两个特征:代理类与被代理类同类型;代理类持有被代理类。
*
*/
//我理解就是扩展的方法在 public class MyInvocationHandler implements InvocationHandler 重写后
//然后在在程序运行期间,jvm内部动态的创建的代理类$Proxy0.java,编译为.class,实例化返回代理对象
public class TestProxy {
public static void main(String[] args) throws Exception {
//设置虚拟机参数
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//1.原生代理
Class.forName("com.mysql.jdbc.Driver");
Connection source = DriverManager.getConnection("jdbc:mysql://localhost:3306/720a", "root", "root");
ClassLoader classLoader = source.getClass().getClassLoader(); //第一个参数 classLoader对象
Class[] clz = {Connection.class}; //第二个参数 被代理类类型
InvocationHandler handler = new MyInvocationHandler(source); //代理类的方法 包括修改过完年未修改的
//o即是$Proxy0的实例化对象
Connection o = (Connection) Proxy.newProxyInstance(classLoader, clz, handler);
PreparedStatement pst = o.prepareStatement("select * from sysuser");//调用toString,其实是调用该对象InvocationHandler的invoke方法
ResultSet resultSet = pst.executeQuery();
resultSet.next();
String uname = resultSet.getString("uname");
System.out.println(uname);
o.close();
}
}
public class MyInvocationHandler implements InvocationHandler {
private Object source = null;
public MyInvocationHandler(Object source){
this.source = source;
}
//代理对象,调用的方法对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println(method.getName());
// System.out.println(Arrays.toString(args));
//重写close,其他方法放行继续调用被代理对象的方法
if("close".equals(method.getName())){
//放回连接池
System.out.println("放回连接池");
return null;
}else{
Object invoke = method.invoke(source, args);
return invoke;
}
}
}
17.延迟加载
延迟加载:用的时候才去服务器端加载对应的数据,提高用户的响应速度
mybatis的延迟加载不重要,重要的时延迟加载的思想(分页,针对前端界面的渲染图片,树形节点的加载,移动端数据列表的延迟加载)
mybatis的延迟加载是针对于association与collection进行级联查询场景。
//一般级联才有延迟加载
//mybatis-config
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启全局的延迟加载-->
<setting name="lazyLoadingEnabled" value="true"></setting>
<!--积极加载改为消极加载-->
//value="true"时会在级联第二次调用的时候会把第二次的结果直接都查出来
//value="false"时会在级联第二次调用的时候只会查需要的那个
<setting name="aggressiveLazyLoading" value="false"></setting>
<!-- 调用toString,equals不触发延迟对象的加载 -->
//这个给 tostring 设置了延迟加载 自己去查文档
<setting name="lazyLoadTriggerMethods" value=""></setting>
</settings>
//Test
@Test
public void test1_lazyLoad(){
SqlSessionFactory factory = SessionFactoryUtil.getFactory();
SqlSession session = factory.openSession();
SysuserMapper mapper = session.getMapper(SysuserMapper.class);
Sysuser sysuser = mapper.selectUserAndRoleByUserid(1);
System.out.println("-------------------------");
System.out.println(sysuser.getSrole()); //懒加载设置后只有在这个调用的时候才会加载这个这个需求的信息
session.close();
}
18.mybatis缓存
mybatis缓存:针对于查询数据.基于内存来存储数据。
场景:查询用户id为1的数据,先去查询内存中的数据,查到的话则直接返回数据;查不到则执行sql查询数据库,把查询结果放到内存中。
带来的问题:数据同步(缓存中的数据与数据库中的数据同步问题).
同步问题解决:(删除或修改数据的时候,还是修改数据库,会把缓存中对应的数据清除)
mybatis的缓存分为一级缓存(基于SqlSession进行缓存),二级缓存(sessionFactory级别)
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
缓存清理算法:
先进先出
最近最少命中
最少命中
//开启二级缓存
//映射文件里
<cache eviction="LRU"
flushInterval="600000"
size="512"
readOnly="true"/>
//Test
@Test
public void test3_factoryCache(){
SqlSessionFactory factory = SessionFactoryUtil.getFactory();
SqlSession session = factory.openSession();
SysuserMapper mapper = session.getMapper(SysuserMapper.class);//是一个代理对象,该代理对象从SysuserMapper派生。
Sysuser sysuser = mapper.selectUserById(1);//先查SqlsesionFactory缓存(key-value),再查数据库
System.out.println("------查询用户--------"+sysuser);
session.close();
SqlSession session2 = factory.openSession();
SysuserMapper mapper2 = session2.getMapper(SysuserMapper.class);
Sysuser sysuser2 = mapper2.selectUserById(1);
System.out.println("------查询用户第二次--------"+sysuser2);
session2.close();
}
19.了解getMapper
了解getMapper:
内部对mapper接口创建代理对象。
注意点:
- 以后建数据库表,起码create_time与update_time必须有,查询排序使用。
- Date:日期类型。数据库使用timestamp类型,可以设置CURRENT_TIMESTAMP默认日期,可以设置根据时间戳更新(updatetime勾选上 )。
- 实体类使用Date,String,建议使用String,如果使用Date到springMVC框架会带来麻烦。
- 在命名(类名,成员变量名,包名,数据库字段名)的时候,不允许出现单个单词。
- 在实体类,方法形参出现变量建议都使用包装类对象。
- private String bName;首字母小写,紧跟字母大写(绝对不要这么写)
- private String isOk;//不允许成员变量名is开头
- 以后建表主键使用varchar,代码使用uid设置主键值。