mybatis框架(持续更新...)
mybatis框架
概述
mybatis是基于java的持久层框架,内部封装了jdbc,使开发者只关注sql语句本身。
mybatis通过xml和注解的方式将各种statement配置起来,并通过Java对象和statement中的动态参数进行映射最终生成sql语句,并对结果映射为java对象并返回
mybatis采用ORM的思想方便操作等
ORM:
Object Relational Mapping 对应关系映射
简单的来说就是:
将数据库中的表和实体类及实体类的属性对应起来
让我们看可以操作实体类就可以实现操作数据库表
表 实体类
user User
id user_id
name user_name
. .
. .
. .
mybatis 入门(xml实现)
创建一个maven项目,直接创建
在pom.xml加入依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
在resource文件夹中创建mybatis的 --主配置-- 文件命名为mysqlMapConfig.xml,
在主配置文件中配置我们的所有javabean对象的一些CRUD操作。
主配置文件主要功能:
1.配置连接的数据库
2.配置文件映射,如:对UserDao的一些操作的文件映射,需要使用Mapper标签
<?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">
<!-- mybatis 的 主配置文件-->
<configuration>
<!-- 配置环境-->
<environments default="mysql">
<!--配置mysql-->
<environment id="mysql">
<!--配置事务类型-->
<transactionManager type="JDBC"/>
<!--配置数据源/连接池-->
<dataSource type="POOLED">
<!--配置连接数据库的基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://xx.xxx.xxx.xxx:3306/xxx"/>
<property name="username" value="xxxx"/>
<property name="password" value="xxx"/>
</dataSource>
</environment>
</environments>
<!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
<mappers>
<mapper resource="club/twzw/dao/UserDao.xml"/>
</mappers>
</configuration>
在resources 文件夹下 创建UserDao.xml(与类名相一致,路径也要一致,映射的配置文件相一致)
主要包括一些CRUD的操作,延迟加载等等。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org.//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="club.twzw.dao.UserDao"> <<<<<------这里的namespace 必须和 接口进行绑定,也就是接口的全类名路径,且各个标签使用的id为接口中定义的名称,接口名为findAll,标签使用的id也为findAll,完成绑定。
<select id="findAll" resultType="club.twzw.domain.User">
select * from user
</select>
</mapper>
在resources 文件夹下 创建log4j.properties文件,用于输入日志文件
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
创建dao处理层接口,用于一些CRUD操作的方法,
package club.twzw.dao;
import club.twzw.domain.User;
import java.util.List;
/**
* 用户持久层接口
*/
public interface UserDao {
/**
* 查询所有操作
*/
List<User> findAll();
}
创建bean对象,用于存储数据
package club.twzw.domain;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String name;
private String born;
private Integer sex;
private String address;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", born='" + born + '\'' +
", sex=" + sex +
", address='" + address + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBorn() {
return born;
}
public void setBorn(String born) {
this.born = born;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
创建测试函数,factory.openSession();为空需要手动提交,factory.openSession(true);为自动提交
package club.twzw.test;
import club.twzw.dao.UserDao;
import club.twzw.domain.User;
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 javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class mybatisTest {
public static void main(String[] args) throws IOException {
// 读取配置文件,为主配置文件
InputStream in = Resources.getResourceAsStream("mysqlMapConfig.xml");
// 创建SqlSessionFactory 工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//使用工厂生产session
SqlSession sqlSession = factory.openSession();
// 使用sqlsession创建dao接口的代理对象,传入接口
UserDao userDao = sqlSession.getMapper(UserDao.class);
// 使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user:users){
System.out.println(user);
}
//释放资源
in.close();
sqlSession.close();
}
}
最终实现效果控制台输出
User{id=1, name='comi', born='1998', sex=0, address='xxxx'}
User{id=2, name='siki', born='2002', sex=1, address=' xxxx'}
mybatis 注意事项
一、创建UserDao.xml和UserDao.java时名称是为了和我们之前的知识保持一致
在mybatis中它把持久层的操作接口名称和映射文件也叫做 Mapper
所以 UserDao 和 UserMapper 是一样的
二、mybatis的映射配置文件位置必须与dao接口的包结构相同
三、映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
四、映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名
当我们遵循二、三、四点后,在开发中便~~~无须实现dao实现类~~~
Sqlsession和connection都是非线程安全的,不能放在成员变量中,每一次使用都应该去获取新的对象
mybatis 引用外部文件配置数据库连接
新建一个dbconfig.properties文件,在主配置文件下 添加以下语句,并且修改配置文件的properties
<properties resource="dbconfig.properties"/>
<environments default="mysql">
<!--配置mysql-->
<environment id="mysql">
<!--配置事务类型-->
<transactionManager type="JDBC"/>
<!--配置数据源/连接池-->
<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>
dbconfig.properties文件内容
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://xx.xx.xx.xx:3306/blog
jdbc.username = blog
jdbc.password = 146325
主配置文件的常用标签
setting
在这里,可以配置相关的全局设置,比如懒加载,驼峰命名等等设置。
typeAliases 别名处理器
可以为我们常用的Java类起别名,让很长的名字变得短,别名不区分大小写。在主配置文件下就为全局配置,在单独的UserDao中就为局部别名。
package可以批量起别名,自动为package下的类自动起别名
<settings>
<!--懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiceLazyLoading" value="false"/>
<!--开启驼峰命名自动转换-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--如果不配置alias就会默认为类名的小写-->
<typeAliases>
<typeAlias type="club.twzw.dao.UserDao" alias="user"/>
</typeAliases>
<!-包下所有类自动起别名-->
<package name="club.twzw.dao"/>
mappers 将sql映射注册到全局配置中 mapper 注册一个sql映射
resource:引用类路径下的sql映射文件
url:引入网络路径或者磁盘路径下的sql映射文件
class:引入(注册)接口
1.有sql文件,sql文件必须和接口同名且在同一目录
2.注解实现
推荐比较重要的Dao接口可以用xml实现,不太重要的可以使用注解实现,提高开发速度
批量注册
使用package标签,注册该包下所有的映射,如果是xml文件,同样,sql文件必须和接口同名且在同一目录,才能实现注册
<package name="club.twxw.dao.UserDao"/>
databaseIdProvider 使用不同的数据库
且每一个标签都有各自的使用顺序,否者会报错。
<databaseIdProvider type="DB_VEBDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
mybatis(注解实现映射)
1.无需在配置UserDao.xml文件
2.将mysqlMapConfig.xml的mapper属性的resource改为class,并配置全路径类名
<mappers>
<mapper class="club.twzw.dao.UserDao"/>
</mappers>
3.将UserDao的findAll方法上加入注解@Select()
@Select("select * from user")
List<User> findAll();
增加代码的复用性
由于每一次使用dao都会有一些重复的过程,我们可以把这些重复的额过程提取出来,不但方便使用,而且代码简洁。
比如说:我们可以在代码运行之前,销毁之前运行一些代码
@Before
public void init() throws IOException {
// 读取配置文件
in = Resources.getResourceAsStream("mysqlMapConfig.xml");
// 创建SqlSessionFactory 工厂
builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
//使用工厂生产session
sqlSession = factory.openSession();
// 使用sqlsession创建dao接口的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After
public void destory() throws Exception{
sqlSession.commit();
//释放资源
in.close();
sqlSession.close();
}
这样我们就可以简化代码,彻底专注于核心业务dao层
@Test
public void testSave() throws IOException {
User user = new User();
user.setAddress("japan");
user.setBorn("1998");
user.setName("gimi");
user.setSex(1);
user.setId(3);
// 使用代理对象执行方法
userDao.addUser(user); <<<-----
//sqlSession.commit(); <<<-----必须要提交事务,否则无法实现CRUD操作,同样,也可以把这一句放在销毁之前,但是这一句便不能再出现
}
当然,还需要再UserDao.xml 中 加入相应的id addUser
<insert id="addUser" parameterType="club.twzw.domain.User">
insert into user (id,name,born,sex,address)values(#{id},#{name},#{born},#{sex},#{address})
</insert>
完善CRUD相关代码及模糊查询
//findById
@Test
public void testDelete(){
User user = userDao.findById(1);
System.out.println(user);
}
//add
@Test
public void testSave() throws IOException {
User user = new User();
user.setAddress("japan");
user.setBorn("1998");
user.setName("gimi");
user.setSex(1);
user.setId(3);
// 使用代理对象执行方法
userDao.addUser(user);
}
//update
@Test
public void testUpdate(){
User user = new User();
user.setId(3);
user.setSex(1);
user.setName("???");
user.setAddress("djkajdjwa");
user.setBorn("20000000000");
userDao.UpdateUser(user);
}
//delete
@Test
public void testDelete(){
userDao.delete(3);
}
// 模糊查询
@Test
public void findByName() {
List<User> users = userDao.findByName("%comii%");
for (User user : users) {
System.out.println(user);
}
}
配置相关xml,namespace 需和类路径相同,与接口进行绑定,且#{xxx}中的名称为JavaBean中的属性名称相同,User中有name,sex,address,那么xml文件中的#{xxx} 也为#{name},#{sex},#{address}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org.//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="club.twzw.dao.UserDao">
<!-- // 有时数据库和java bean 中的命名不一致,为了方便起见,我们可以配置resultMap(结果集)-->
<!-- // 配置查询结果的列名和实体类的属性名对应的关系(如果命名不一致,一致无需创建)-->
<resultMap id="userMap" type="club.twzw.domain.User">
<!-- 主键对应字段-->
<id property="id" column="id"></id>
<!-- 非主键对应字段-->
<result property="name" column="name"></result>
<result property="born" column="born"></result>
<result property="sex" column="sex"></result>
</resultMap>
<!--使用resultMap-->
<!--查询所有-->
<select id="findAll" resultMap="userMap">
select * from user
</select>
<!--不使用resultMap-->
<!--查询所有-->
<select id="findAll" resultType="club.twzw.domain.User">
select * from user
</select>
<!--查询单个-->
<select id="findById" parameterType="int" resultType="club.twzw.domain.User">
select * from user where id = #{id}
</select>
<!--添加用户-->
<insert id="addUser" parameterType="club.twzw.domain.User">
insert into user (id,name,born,sex,address)values(#{id},#{name},#{born},#{sex},#{address})
</insert>
<!--更新用户数据-->
<update id="UpdateUser" parameterType="club.twzw.domain.User">
update user set name=#{name},address=#{address},sex=#{sex},born=#{born} where id=#{id}
</update>
<!--删除用户数据-->
<delete id="delete" parameterType="Integer">
delete from user where id=#{id}
</delete>
<!--根据name模糊查询用户-->
<select id="findByName" parameterType="string" resultType="club.twzw.domain.User">
select * from user where name like #{name}
</select>
</mapper>
在插入完成之后获取ID的值
1.通过selectKey标签 设置resultType,keyProperty,keyColumn,order 相关设置,自动返回数据。(不支持自增的数据库使用的方法,Oracle),order=“BEFORE” 在执行插入之前
2.使用useGeneratedKeys=“true” keyProperty=“id”,这里的id 为JavaBean的id(支持自增的数据库,mysql)
<!--方法一-->
<insert id="addUser" parameterType="club.twzw.domain.User">
<selectKey resultType="int" keyProperty="int" keyColumn="id" order="BEFORE">
<!--查询主键id,得到后便可以使id有值-->
select last_intsert_id();
</selectKey>
insert into user (id,name,born,sex,address)values(#{id},#{name},#{born},#{sex},#{address})
</insert>
<!--方法二-->
<insert id="addUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
insert into user (name,born,sex,address)values(#{name},#{born},#{sex},#{address})
</insert>
OGNL表达式(object graphic navigation language)
通过对象的取值方法 来获取数据,在写法上省略了get(省略相关配置)
eg:
类: User.GetName();
ONNL: User.name
使用alias 配置别名
<!-- 使用alias配置别名-->
<!-- <typeAliases>-->
<!-- <typeAlias type="club.twzw.domain.User" alias="user"></typeAlias>-->
<!-- </typeAliases>-->
<!-- 那么所有的club.twzw.domain.User 便可以用 user 替代-->
<!-- <select id="findById" parameterType="int" resultType="user">-->
<!-- select * from user where id = #{id}-->
<!-- </select>-->
mtbatics 中的动态sql语句查询 -if
UserDao.xml 中添加
<!-- 根据条件查询-->
<select id="findByCondition" resultType="club.twzw.domain.User" parameterType="club.twzw.domain.User">
select * from user where 1 = 1 <<<<<----------
<if test="name != null">
and name = #{name}
</if>
</select>
UserDao.java 中添加
boolean findByCondition(User user);
测试代码
@Test
public void findByCondition() {
User u = new User();
u.setName("comi");
User u = userDao.findByCondition(u);
System.out.println(u);
}
不使用 where 1= 1,-where
<!--使用where 标签-->
<select id="findByCondition" resultType="club.twzw.domain.User" parameterType="club.twzw.domain.User">
select * from user <<<<<----------
<where> <<<<<----------
<if test="name != null">
and name = #{name}
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</where>
</select>
将重复使用的 sql 语句,抽取出来,重复使用
<!--由于我们经常使用 select * from user 这条语句,所以我们可以把这条语句抽取出来-->
<sql id="selectUserAll"> <<<<<----------
select * from user;
</sql>
<!-- 使用 sql-->
<select id="findAll" resultType="club.twzw.damain.User">
<include refid="selectUserAll"></include> <<<<<----------
</select>
查询多个指定用户,-foreach
sql 语句 select * from user where id in (41,42,43);
mybatis 实现
<!-- 查询多个指定id的数据-->
<select id="findByUsersIds" resultType="club.twzw.damain.User" parameterType="club.twzw.damain.User">
select * from user
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open=" and ids in (" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
单个参数和多个参数
如果是单个参数或者javaBe对象,mybatis会自动识别,无需特别处理,但是如果是多个参数,则需要特殊处理
使用@Param注解,在接口中使用
boolean addUser(@Param("id")Interger id,@Param("name")String name);
在xml中直接使用key即可
<select id="findByCondition" resultMap="userMap">
select * from user where id = #{id} and name =#{name}
</select>
将不相关的数据进行封装成一个参数
java代码中
Map<String,Object> map = new HashMap<>();
map.put("id",1);
map.put("name","comi");
User u = userDao.findByCondition(mao);
xml就可以直接使用#{id},#{name}
<select id="findByCondition" resultMap="userMap">
select * from user where id = #{id} and name =#{name}
</select>
select 标签
1.使用select 封装 List,如果返回数据为多个,mybatis会自动封装返回的数据为list
2.使用select 封装 Map,resultMap 设置为Map(单个数据)
3.使用select 封装 Map,在方法上添加 @MapKey(“id”) 指定 key,就可以自动封装
@MapKey("id")
public Map<Interger,User> map getUserByCondition(String name);
resultMap 自定义结果集(如果数据库和JavaBean数据名称对应不一致)
<resultMap id="userMap" type="club.twzw.domain.User">
<!-- 主键对应字段-->
<id property="id" column="Id"></id>
<!-- 非主键对应字段-->
<result property="name" column="Name"></result>
<result property="born" column="Born"></result>
<result property="sex" column="Sex"></result>
</resultMap>
resultMap中返回的结果中一个类包含另一个类的情况(联合查询)
有两个JavaBean对象,一个是User,一个是account,在查询user时需要查询account。
user
----id
----name
----gender
----acc
account
----id
----money
进行设置结果集
<!--方法一-->
<resultMap id="userMap" type="club.twzw.domain.User">
<!-- 主键对应字段-->
<id property="id" column="Id"></id>
<!-- 非主键对应字段-->
<result property="name" column="Name"></result>
<result property="born" column="Born"></result>
<result property="gender" column="Gender"></result>
<result property="aid" column="account.id"></result> <<<<---------
<result property="amonty" column="account.money"></result> <<<<---------
</resultMap>
<!--方法二-->
<!--property 指的是 要封装的JavaBean中的属性是哪一个-->
<!--javaType 这个属性对象的类型,即account-->
<association property="acc" column="id" javaType="club.twzw.domain.account">
<result property="aid" column="id"></result> <<<<---------
<result property="amonty" column="money"></result> <<<<---------
</association>
mybatis 延迟加载和直接加载
什么是延迟加载?
在真正使用数据时,才发起查询,不用的时候不查询,按需加载(懒加载)
什么是立即加载?
不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:一对一,多对一(mytatis 没有这个概念),一对多,多对多。不同情况使用不同的加载策略。
一对多,多对多:采用延迟加载
一对一,多对一:采用立即加载
配置延迟加载
全局配置文件中加入
<!--在这里配置相关懒加载参数-->
<!--这样 懒加载的配置就配置好了-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiceLazyLoading" value="false"/>
</settings>
在UserDao.xml 中配置 resultMap的association标签属性,重点是select 属性,必须是namespace加方法名,唯一标识用户,通过此方法去查出信息
<!--配置延迟加载-->
<resultMap id="userMap" type="club.twzw.domain.User">
<!--主键对应字段-->
<id property="id" column="id"></id>
<!--非主键对应字段-->
<result property="name" column="name"></result>
<result property="born" column="born"></result>
<result property="sex" column="sex"></result>
<!--使用association 标签-->
<!--select 属性指定的内容是查询用户的唯一标识-->
<!--同样 column 也是必须的,否则无法运行,会报错-->
<!--这样我们就可以配置相关连的对象了,比如说用户的账户信息等!-->
<association property="user" column="id" javaType="user" select="club.twzw.dao.UserDao.findById"> <<<<<---------
</association>
</resultMap>