您的位置:首页 > 其它

零基础入门Mybatis实战教程(基础篇)

2020-06-28 05:19 246 查看


文章目录

一、什么是Mybatis

  • MyBatis 是一款优秀的持久层框架
  • 它支持定制化 SQL、存储过程以及高级映射。
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。#
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

二、持久化

(一)什么是持久化
持久化:持久化就是将有用的数据以某种技术保存起来,将来可以再次取出来应用(Redis内存数据以文件的形式保存在永久介质中(磁盘等)都是持久化的例子)

(二)为什么需要持久化?

  • 对于数据需要将其进行存储,不能让其被丢掉。
  • 内存:断电即失,且价格昂贵
  • 通过持久化技术可以减少访问数据库数据次数,增加应用程序执行速度
  • 代码重用性高,能够完成大部分数据库操作
  • 松散耦合,使持久化不依赖于底层数据库和上层业务逻辑实现,更换数据库时只需修改配置文件而不用修改代码

三、数据源和连接池

(一)数据源
所谓数据源也就是数据的来源。它存储了所有建立数据库连接需要的信息,是提供某种所需要数据的器件或原始媒体。
在数据源中存储了所有建立数据库连接的信息。就像通过指定文件名称可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库连接算是对数据库的一个抽象映射,即一个数据源对于一个数据库。

数据源通常有以下属性

  • portNumber:数据库服务器使用的端口。
  • databaseName String:数据库名称,即数据库的SID。
  • dataSourceName String:数据源接口实现类的名称。
  • description String :对数据源的描述。
  • networkProtocol String :和服务器通讯使用的网络协议名。
  • user String :用户登录名。
  • password String :用户登录密码。
  • serverName String:数据库服务器名称。

如果数据是水,数据库就是水库,数据源就是连接水库的管道,终端用户看到的数据集是管道里流出来的水。

(二)连接池
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。
当一个线程需要用JDBC对一个数据库操作时,将从池中请求一个连接。当这个连接使用完毕后,将返回到连接池中,等待为其他的线程服务。 连接池的主要优点有以下三个方面。

  • 减少连接创建时间。连接池中的连接是已准备好的、可重复使用的,获取后可以直接访问数据库,因此减少了连接创建的次数和时间。
  • 简化的编程模式。当使用连接池时,每一个单独的线程能够像创建一个自己的JDBC连接一样操作,允许用户直接使用JDBC编程技术。
  • 控制资源的使用。如果不使用连接池,每次访问数据库都需要创建一个连接,这样系统的稳定性受系统连接需求影响很大,很容易产生资源浪费和高负载异常。连接池能够使性能最大化,将资源利用控制在一定的水平之下。连接池能控制池中的连接数量,增强了系统在大量用户应用时的稳定性。

四、JDBC

    在早期java开发中,如果要使java连接数据库,就需要安装相应数据库的连接驱动,而各个不同的数据库的驱动实现是大不相同的,因此各数据库之间的差异很大,使得开发人对数据库的连接十分繁琐,因此JDBC应运而生,它统一了所有数据库的连接方式,各个数据库仅需要对JDBC提供相应的实现,而开发人员直接连接JDBC即可轻松地连接各种不同的数据库。
(一)JDBC是什么
    JDBC(Java DataBase Connectivity),是一种用于执行SQL语句的Java 接口,由一组用Java语言编写的类和接口组成,是Java和数据库之间的一个桥梁,是一个规范而不是一个特定的实现,而由各个数据库提供相应的实现。

(二)建立JDBC和数据库之间的Connection连接
这里需要提供:数据库服务端的IP地址:127.0.0.1
                         数据库的端口号: 3306
                         数据库名称 DBName
                         编码方式 UTF-8
                         账号 root
                         密码 admin

Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8", "root", "admin");

(三)创建Statement或者PreparedStatement接口,执行SQL语句

  • 使用Statement接口

Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。其中 ,增删改只需要改变SQL语句的内容就能完成,然而查询略显复杂。在Statement中使用字符串拼接的方式,该方式存在句法复杂,容易犯错等缺点,具体在下文中的对比中介绍。所以Statement在实际过程中使用的非常的少,所以具体的放到PreparedStatement那里给出详细代码。

Statement s = conn.createStatement();
// 准备sql语句
// 注意: 字符串要用单引号'
String sql = "insert into t_courses values(null,"+"'数学')";
//在statement中使用字符串拼接的方式,这种方式存在诸多问题
s.execute(sql);
System.out.println("执行插入语句成功");
  • 使用PreparedStatement接口

与 Statement一样,PreparedStatement也是用来执行sql语句(含占位符)的与创建Statement不同的是,需要根据sql语句创建PreparedStatement。除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。

String sql = "insert into t_course(course_name) values(?)";
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, courseName); //给占位符赋值
pstmt.executeUpdate();			//执行

差异:
1、使用PreparedStatement时,是采用占位符的方式“?”在这里就起到占位符的作用。这种方式除了避免了statement拼接字符串的繁琐之外,每次SQL语句都是一样的,java类就不会再次编译,且这里的参数索引是从1开始的。
2、增删改都使用pstmt.executeUpdate();语句进行SQL语句的提交下文的查询。
3、 PreparedStatement接口提供了相应的批量操作的方法。

for(int i=1;i<100;i++){
pstmt.setInt(1,8000+i);
pstmt.setString(2,"赵_"+i);
pstmt.addBatch();
//批量更新
if(i%10==0){
pstmt.executeBatch();
}

PreparedStatement的优点:
1、其使用参数设置,可读性好,不易记错。在statement中使用字符串拼接,可读性和维护性比较差。
2、其具有预编译机制,性能比statement更快。
3、其能够有效防止SQL注入攻击。

(四)处理和显示结果
执行查询语句,并把结果集返回给集合ResultSet

ResultSet rs = s.executeQuery(sql);

利用While(ResultSet.next()){…}循环将集合ResultSet中的结果遍历出来。每个记录对应一个对象

while (rs.next()){
int courseId = rs.getInt("course_id");
String courseName = rs.getString("course_name");
//每个记录对应一个对象
Course course = new Course();
//在我的项目中创建了course类,其中定义了set方法,所以这里将查询到的值传给了course,也可以直接打印到控制台
course.setCourseId(courseId);
course.setCourseName(courseName);
//将对象放到集合中
courseList.add(course);
}

(五)释放资源
在JDBC编码的过程中我们创建了Connection、Statement、ResultSet等资源,这些资源在使用完毕之后是一定要进行关闭的。关闭的过程中遵循从里到外的原则。这里我将这些关闭的操作写成一个方法和建立连接的方法一起放到一份工具类中

public static void close(PreparedStatement pstmt){
if(pstmt != null){						//避免出现空指针异常
try{
pstmt.close();
}catch(SQLException e){
e.printStackTrace();
}

}
}

public static void close(Connection conn){
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}

public static void close(ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}

五、Mybatis执行过程

生命周期,和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。

<img src="链接#pic_center"width=“百比例”>
(一)SqlSessionFactoryBuilder

  • SqlSessionFactory利用配置文件创建SqlSessionFactory
    (二)SqlSessionFactory:
  • 可以想象为:数据库连接池
  • SqlSessionFactory 一旦被创建就会在应用运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • 因此SqlSessionFactory的最佳作用域是应用作用域。
  • 最简单的就是使用 单例模式 或者静态单例模式
    (三)SqlSession
  • 连接到连接池的一个请求
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 用完之后需要赶紧关闭,否则会一直占用资源!

    这里的每一个Mapper,就代表一个具体的业务!

六、第一个Mybatis程序

(一)导入Mybatis依赖

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>

(二)创建数据库

CREATE DATABASE `mybatis`;
use `mybatis`;
CREATE TABLE `user`(
`id` INT(20) not null PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO `user` (`id`,`name`,`pwd`) VALUES
(1,'狂神','123456'),
(2,'张三','123456'),
(3,'李四','123890')

(三)配置核心配置文件

<?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核心配置文件-->
<configuration>
<!--environments配置环境组-->
<!--default默认环境-->
<environments default="development">
<!--environment单个环境-->
<environment id="development">
<!--transactionManager配置事务管理器-->
<transactionManager type="JDBC"/>
<!--配置数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>

(四)编写mybatis工具类

//sqlSessionFactory--->SessionFactory
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try{
//使用mybatis第一步、获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch(IOException e) {
e.printStackTrace();
}
}

//既然有了 SqlSessionFactory,顾名思义,我们就可以从中获得 SqlSession 的实例了。
// SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}

(五)编写实体类

package com.rui.pojo;

@Data
@AllConstruction
@NoConstruction
public class user {
private int id;
private String name;
private String pwd;
}

(六)编写Dao层
1、Dao接口

package com.rui.dao;

import com.rui.pojo.User;

import java.util.List;

public interface UserDao {
List<User> getUserList();
}

2、Mapper配置文件(由原先的DaoImpl变换为)

<?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接口-->
<mapper namespace="com.nan.dao.UserDao">

<!--select查询语句-->
<select id="getUserList" resultType="com.nan.pojo.User">
/*定义sql*/
select * from mybatis.user
</select>
</mapper>

(七)在配置文件中配置Mapper信息

<mappers>
<mapper class="com.nan.mapper.UserMapper"/>
</mappers>

(八)测试

public class UserDaoTest {
@Test
public void test(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//执行SQL
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
}

七、CRUD功能实现

  • id:就是对应的namespace中的方法名;
  • resultType:Sql语句执行的返回值!
  • parameterType:参数类型!

(一)namespace

  • namespace=绑定一个对应的Dao接口
  • namespace中的包名要和Dao/mapper接口的包名保持一致

(二)Select

<select id="getUserById" resultType="com.rui.pojo.User" parameterType="int">
/*定义sql*/
select * from mybatis.user where id = #{id};
</select>

(三)Insert

<insert id="addUser" parameterType="com.nan.pojo.User">
/*定义sql*/
insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
</insert>

(四)Update

<update id="updateUser" parameterType="com.nan.pojo.User">
update mybatis.user set name = #{name},pwd=#{pwd} where id =#{id};

(五)Delete

<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id};
</delete>

注意点:增删改需要提交事务
(六)解决属性名和字段名不一致的问题(ResultMap)
1、问题描述
数据库中的字段

新建一个项目,拷贝之前的,测试实体类**字段(密码)**不一致的情况。

public class User {
private int id;
private String name;
private String password;
}

2、解决方法
(1)起别名

select id,name,pwd as password from mybatis.user where id = #{id}

(2)ResultMap结果集映射

<!--结果集映射-->
<resultMap id="UserMap" type="User">
<!--column数据库中的字段,property实体类中的属性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="UserMap" parameterType="int">
/*定义sql*/
select * from mybatis.user where id = #{id};
</select>
  • ResultMap 元素是 MyBatis 中最重要最强大的元素
  • ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。

(七)多对一关系处理
1、按照查询嵌套处理

<!--
思路:
1、查询所有的学生信息
2、根据查询出来的学生的id的tid,寻找对应的老师! -子查询
-->

<select id="getTeacher" resultType="com.rui.pojo.Teacher">
select * from teacher where id = #{id}
</select>

<resultMap id="StudentTeacher" type="com.rui.pojo.Student">
<!--复杂的属性,我们需要单独处理  对象:association  集合:collection-->
<association property="teacher" column="tid" javaType="com.rui.pojo.Teacher" select="getTeacher"/>
</resultMap>

<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>

2、按照结果嵌套处理

<!--按照结果嵌套处理-->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id;
</select>

<resultMap id="StudentTeacher2" type="com.rui.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="com.rui.pojo.Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
</association>

</resultMap>

(八)一对多关系处理
1、按照查询嵌套处理

<select id="getTeacher2" resultMap="TeacherStudent2">
select * from mybatis.teacher where id = #{tid}
</select>
<resultMap id="TeacherStudent2" type="com.rui.pojo.Teacher">
<collection property="students" javaType="ArrayList" ofType="com.rui.pojo.Student" select="getStudentByTeacherId" column="id"/>
</resultMap>

<select id="getStudentByTeacherId" resultType="com.rui.pojo.Student">
select * from mybatis.student where tid = #{tid}
</select>

2、按照结果嵌套处理

<!--按结果嵌套查询-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid
from student s,teacher t
where s.tid=t.id and t.id = #{tid}
</select>
<resultMap id="TeacherStudent" type="com.rui.pojo.Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--复杂的属性,我们需要单独处理  对象:association  集合:collection
javaType="" 指定属性的类型
集合中的泛型信息,我们使用ofType获取
-->
<collection property="students" ofType="com.rui.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>

(九)万能的Map(作用是便于插入数据)
我们的实体类,或者数据库中的表**,字段或者参数过多**,我们应当考虑使用Map,Map的好处在于我们可以只传入需要的属性即可,不需要如User对象一样将所有属性赋值

public void addUser2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<>();
map.put("userId",4);
map.put("userName","王五");
map.put("password","23333");
mapper.addUser2(map);
//提交事务
sqlSession.commit();
sqlSession.close();
}
  • Map传递参数,直接在sql中取出key即可!【parameterType=“map”】
  • 对象传递参数,直接在sql中取对象的属性即可!【parameterType=“Object”】

(十)思考(模糊查询怎么写?)
1、Java代码执行的时候传递通配符%%

List<User> userList=mapper.getUserLike("%李%");

2、在sql拼接中使用通配符!(防止sql注入,更安全)

select * from mybatis.user where name like "%"#{value}"%"

参考:
[1] 【狂神说Java】Mybatis最新完整教程IDEA版通俗易懂
[2] JDBC详细介绍

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: