ORM
常见的ORM框架有Hibernate和Mybaits
Mybatis快速入门
创建一个空的maven项目 【参考jdbcTest项目】
1. 导入开发包或者依赖
- Mybatis
- mysql-connecter-java
- log4j (日志系统,日志框架,“是真正的日志实现”)
- commons-logging (日志接口)
同时创建数据库
2. 准备工作(pojo,DB,配置logger)
-
创建数据库
-
创建实体类
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <param name="Encoding" value="UTF-8" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /> </layout> </appender> <logger name="java.sql"> <level value="debug" /> </logger> <logger name="org.apache.ibatis"> <level value="info" /> </logger> <root> <level value="debug" /> <appender-ref ref="STDOUT" /> </root> </log4j:configuration>
3.Mybatis的全局配置文件
-
配置连接环境
-
建立与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">
<configuration>
<!--加载类路径下的属性文件-->
<properties resource="db.properties" />
<!--设置一个默认的连接环境-->
<environments default="mysql_first">
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="mysql_first">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc" />
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性 -->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
4.创建实体类与映射文件Mapper的关系
- 名为StudentMapper.xml
- ResultMap标签:映射实体与表
- 增删改查的标签:insert,delete,update,select
<?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="StudentID">
<!-- resultMap标签:映射实体与表
type属性:表示实体全路径名
id属性:为实体与表的映射取一个任意的唯一的名字
-->
<resultMap type="po.Student" id="studentResult">
<!-- id标签:映射主键属性
result标签:映射非主键属性
property属性:实体的属性名
column属性:表的字段名
-->
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sal" column="sal"/>
</resultMap>
<!--
parameterType: 传入的参数类型可以是 实体类(dao.student),int,map等
resultMap:返回值类型,就是上面的resultMap的id studentResult
当数据库数据转化为pojo对象时,列名和属性名不一致时,通过resultMap做一个映射。
如果返回的是List<Student>,返回的类型讲道理是List<Student>的,但我们只要写集合中的类型就行了,也就是只写Student
-->
<select id="findById" parameterType="int" resultMap="studentResult">
SELECT * FROM STUDENTS WHERE id = #{id};
</select>
<!--在JDBC中我们通常使用?号作为占位符,而在Mybatis中,我们是使用#{}作为占位符
#{}实际上就是调用了Student属性的get方法
-->
<insert id="add" parameterType="po.Student">
INSERT INTO STUDENTS (ID, NAME, SEX) VALUES (#{id},#{name},#{sex});
</insert>
</mapper>
5.编写工具类测试连接
基本使用步骤
Mybatis的API来创建一个工具类,通过mybatis配置文件与数据库的信息,得到Connection对象
-
通过Reader对象读取Mybatis配置文件
Reader reader = Resources.getResourceAsReader("mybatis.xml");
-
通过SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
-
通过SqlSessionFactory获得连接对象SqlSession
Connection和SqlSession都继承自AutoCloseable
SqlSession sqlSession = sqlSessionFactory.openSession(); // sqlSession可以理解为connection对象 //sqlSession.findList(nameSapce.id,参数) 可以理解为获得是statement对象并执行得到结果集
编写工具类
将连接对象绑定到线程上
/**
* 工具类
* @author AdminTC
*/
public class MybatisUtil {
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
private static SqlSessionFactory sqlSessionFactory;
/**
* 加载位于src/mybatis.xml配置文件
*/
static{
try {
Reader reader = Resources.getResourceAsReader("mybatis.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 禁止外界通过new方法创建
*/
private MybatisUtil(){}
/**
* 获取SqlSession
*/
public static SqlSession getSqlSession(){
//从当前线程中获取SqlSession对象
SqlSession sqlSession = threadLocal.get();
//如果SqlSession对象为空
if(sqlSession == null){
//在SqlSessionFactory非空的情况下,获取SqlSession对象
sqlSession = sqlSessionFactory.openSession();
//将SqlSession对象与当前线程绑定在一起
threadLocal.set(sqlSession);
}
//返回SqlSession对象
return sqlSession;
}
/**
* 关闭SqlSession与当前线程分开
*/
public static void closeSqlSession(){
//从当前线程中获取SqlSession对象
SqlSession sqlSession = threadLocal.get();
//如果SqlSession对象非空
if(sqlSession != null){
//关闭SqlSession对象
sqlSession.close();
//分开当前线程与SqlSession对象的关系,目的是让GC尽早回收
threadLocal.remove();
}
}
/**
* 测试
*/
public static void main(String[] args) {
Connection conn = MybatisUtil.getSqlSession().getConnection();
System.out.println(conn!=null?"连接成功":"连接失败");
}
}
6.编写Dao
即respository
public class StudentDao {
public void add(Student student) throws Exception {
//得到连接对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
sqlSession.insert("StudentID.add", student);
//Mybatis中的事务是默认开启的,因此我们在完成操作以后,需要我们手动去提交事务!
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
//测试
public static void main(String[] args) throws Exception {
StudentDao studentDao = new StudentDao();
Student student = new Student(3, "zhong3", "男");
studentDao.add(student);
}
}
查询所有数据的例子
Mapper和dao如下:
sqlSession.selectList()
<!--
查询所有数据
返回值类型讲道理是List<Student>的,但我们只要写集合中的类型就行了
-->
<select id="findAll" resultMap="studentMap">
SELECT * FROM STUDENTS;
</select>
public List<Student> findAll() throws Exception {
//得到连接对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
return sqlSession.selectList("StudentID.findAll");
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
7.Mapper代理方式创建DAO
问题:
Mapper代理方式的意思就是:程序员只需要写dao接口,dao接口实现对象由mybatis自动生成代理对象。
经过我们上面的几篇博文,我们可以发现我们的DaoImpl是十分重复的...
1 dao的实现类中存在重复代码,整个mybatis操作的过程代码模板重复(先创建sqlsession、调用sqlsession的方法、关闭sqlsession)
2、dao的实现 类中存在硬编码,调用sqlsession方法时将statement的id硬编码。
7.1创建Mapper接口
创建Mapper接口,并且建立与映射文件的关系:
-
mapper.xml中namespace指定为mapper接口的全限定名
- 此步骤目的:通过mapper.xml和mapper.java进行关联
-
mapper.xml中statement的sql的id就是mapper.java中方法名
-
mapper.xml中statement的parameterType和mapper.java中方法输入参数类型一致
-
mapper.xml中statement的resultType或resultMap和mapper.java中方法返回值类型一致.
再次说明:statement就是我们在mapper.xml文件中命名空间+sql指定的id
7.2编写测试
public class TestMapperService {
public static void main(String[] args) {
SqlSession sqlSession= MybatisUtil.getSqlSession();
// 由mybatis生成代理对象
StuedentMapper mapper=sqlSession.getMapper(Mapper.StuedentMapper.class);
student st =new student(null,null,30);
List<student> list=mapper.findByCondition(st);
for (student stt: list)
{
System.out.println("name"+stt.getName());
System.out.println("age:"+stt.getAge());
}
}
}
Mybatis分页
- 分页是需要多个参数的。当需要接收多个参数的时候,我们使用Map集合来装载!
编写dao
Map<String, Object> map = new HashMap();
map.put(“start”, start);
map.put(“limit”, limit);
public List<Student> pagination(int start ,int limit) throws Exception {
//得到连接对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
/**
* 由于我们的参数超过了两个,而方法中只有一个Object参数收集
* 因此我们使用Map集合来装载我们的参数
*/
Map<String, Object> map = new HashMap();
map.put("start", start);
map.put("limit", limit);
return sqlSession.selectList("StudentID.pagination", map);
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
public static void main(String[] args) throws Exception {
StudentDao studentDao = new StudentDao();
List<Student> students = studentDao.pagination(0, 3);
for (Student student : students) {
System.out.println(student.getId());
}
}
编写map
parameterType=“map”
<!--分页查询-->
<select id="pagination" parameterType="map" resultMap="studentMap">
/*根据key自动找到对应Map集合的value*/
select * from students limit #{start},#{limit};
</select>
动态SQL
例如:多条件查询的时候,以往我们会通过拼接查询条件的方式来获得查询结果,然而这样干的话,就非常容易出错的
而mybatis内部就有动态SQL的功能【动态SQL就是自动拼接SQL语句】!
通过标签标签等实现
https://segmentfault.com/a/1190000013661958
动态查询
大于符号和小于符号><
直接写编译不成功,必须用大于号用 >
小于号用<
mapper:
<select id="findByCondition" resultMap="studentMap" parameterType="map">
select * from students
<where>
<if test="name!=null">
name=#{name}
</if>
<if test="sal!=null">
and age < #{age}
</if>
</where>
</select>
dao:
public List<Student> findByCondition(String name,Interge age) throws Exception {
//得到连接对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
/**
* 由于我们的参数超过了两个,而方法中只有一个Object参数收集
* 因此我们使用Map集合来装载我们的参数
*/
Map<String, Object> map = new HashMap();
map.put("name", name);
map.put("age", age);
return sqlSession.selectList("StudentID.findByCondition", map);
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
public static void main(String[] args) throws Exception {
StudentDao studentDao = new StudentDao();
List<Student> students = studentDao.findByCondition(null,12);
for (Student student : students) {
System.out.println(student.getId() + "---" + student.getName() + "----" + student.getAge());
}
}
动态更新(set)
<!--动态更新
set 主要是用于解决修改操作中 SQL 语句中可能多出逗号的问题
-->
<!--不要忘了逗号-->
<update id="updateByConditions" parameterType="map">
update students
<set>
<if test="name!=null">
name = #{name},
</if>
<if test="sal!=null">
sal = #{sex},
</if>
</set>
where id = #{id}
</update>
sqlSession.update("StudentID.updateByConditions", map);
动态删除(foreach)
例如:
delete from students where id in (1,2,5,9);
<delete id="deleteByConditions" parameterType="int">
<!-- foreach用于迭代数组元素
open表示开始符号
close表示结束符合
separator表示元素间的分隔符
item表示迭代的数组,属性值可以任意,但提倡与方法的数组名相同
#{ids}表示数组中的每个元素值
-->
delete from students where id in
<foreach collection="array" open="(" close=")" separator="," item="ids">
#{ids}
</foreach>
</delete>
public void deleteByConditions(int... ids) throws Exception {
//得到连接对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
/**
* 由于我们的参数超过了两个,而方法中只有一个Object参数收集
* 因此我们使用Map集合来装载我们的参数
*/
sqlSession.delete("StudentID.deleteByConditions", ids);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
public static void main(String[] args) throws Exception {
StudentDao studentDao = new StudentDao();
studentDao.deleteByConditions(2,3,4);
}
动态插入(trim)
使用情况如下:
Mapper:
SQL代码块是不能帮我们自动去除多余的逗号的,因此我们需要使用trim标签来自己手动去除…
Trim 可以在条件判断完的 SQL 语句前后 添加或者去掉指定的字符
prefix: 添加前缀
prefixOverrides: 去掉前缀
suffix: 添加后缀
suffixOverrides: 去掉后缀
<!--SQL片段默认是不帮我们自动生成合适的SQL,因此需要我们自己手动除去逗号-->
<sql id="key">
<trim suffixOverrides=",">
<if test="id!=null">
id,
</if>
<if test="id!=null">
name,
</if>
<if test="id!=null">
sex,
</if>
</trim>
</sql>
<sql id="value">
<trim suffixOverrides=",">
<if test="id!=null">
#{id},
</if>
<if test="id!=null">
#{name},
</if>
<if test="id!=null">
#{sex},
</if>
</trim>
</sql>
<!--动态插入-->
<insert id="insertByConditions" parameterType="zhongfucheng.Student">
insert into students (<include refid="key"/>) values
(<include refid="value"/>)
</insert>
Dao:
测试三个不同内容的数据
public void insertByConditions(Student student) throws Exception {
//得到连接对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
sqlSession.insert("StudentID.insertByConditions", student);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
sqlSession.rollback();
throw e;
}finally{
MybatisUtil.closeSqlSession();
}
}
public static void main(String[] args) throws Exception {
StudentDao studentDao = new StudentDao();
studentDao.insertByConditions(new Student(55, null, null));//name和sal为空
studentDao.insertByConditions(new Student(66, "haxi", null));//sal为空
studentDao.insertByConditions(new Student(77, null, "男"));//name为空
}