SSM框架
web层 springMVC
业务层 spring
持久层 MyBatis
jdbc 程序回顾
-
注册驱动
-
获得连接
-
创建预编译sql语句对象
-
设置参数, 执行
-
处理结果
-
释放资源
-
public static void main(String[] args) { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { //1.加载数据库驱动 Class.forName("com.mysql.jdbc.Driver"); //2.通过驱动管理类获取数据库链接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "123456"); //3.定义 sql 语句 ?表示占位符 String sql = "select name from user where username = ?"; //4.获取预处理 statement preparedStatement = connection.prepareStatement(sql); //5.设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的参数值 preparedStatement.setString(1, "王五"); //6.向数据库发出 sql 执行查询,查询出结果集 resultSet = preparedStatement.executeQuery(); //7.遍历查询结果集 while (resultSet.next()) { System.out.println(resultSet.getString("id") + " "+resultSet.getString(" username")); } } catch (Exception e) { e.printStackTrace(); } finally { //8.释放资源 if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
jdbc 问题分析
-
数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
-
Sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大, sql 变动需要改变java 代码。
-
使用 preparedStatement 向占有位符号传参数存在硬编码,因为 sql 语句的 where 条件不一定,可能多也可能少,修改 sql 还要修改代码,系统不易维护。
-
对结果集解析存在硬编码(查询列名), sql 变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成 pojo 对象解析比较方便
-
MyBatis: 持久层的一个框架, 封装了JDBC. 操作数据库
-
为什么要学习MyBatis?
-
JDBC那一套代码和DBUtils都有一些很明显的缺点, JDBC和DBUtils不适合做项目
-
MyBatis是工作里面的主流的持久层框架, 使用几率特别大
-
Mybatis快速入门
1.需求
- 使用MyBatis查询所有的用户, 封装到List集合
2.分析
-
创建Maven工程(java), 添加mybatis的依赖坐标
-
创建pojo (javabean)
-
创建UserDao接口
-
创建UserDao映射文件 (xml配置文件)
-
创建MyBatis核心配置文件SqlMapConfig.xml (xml配置文件)
-
编写java代码测试
-
CREATE DATABASE mybatis_day01; USE mybatis_day01; CREATE TABLE t_user( uid int PRIMARY KEY auto_increment, username varchar(40), sex varchar(10), birthday date, address varchar(40) ); INSERT INTO `t_user` VALUES (null, 'zs', '男', '2018-08-08', '北京'); INSERT INTO `t_user` VALUES (null, 'ls', '女', '2018-08-30', '武汉'); INSERT INTO `t_user` VALUES (null, 'ww', '男', '2018-08-08', '北京');
创建Maven工程(jar)导入坐标
<!--1. 添加依赖--> <dependencies> <!--MyBatis坐标--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version> </dependency> <!--单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <!--lombok 依赖--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.18</version> </dependency> </dependencies>
创建User实体类
package com.itheima.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int uid;
private String username;
private String sex;
private Date birthday;
private String address;
}
创建 UserDao 接口
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
/**
* 查询所有
* @return
*/
List<User> findAll();
}
<?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">
<!--
这个文件其实就是我们dao的映射文件,通俗的讲,就是当我们要调用UserDao里面的findAll方法的时候
要执行什么样的sql语句,要在这里做配置!
1. 有接口,就有映射文件
2. 映射文件是一个xml文件,要放在resource文件夹里面,需要为它创建一个目录
3. 记住一下套路:
a. xml的目录最好要和dao接口的包名路径一样!
b. xml的目录创建的时候必须要使用 / 的形式,不能使用 . 的形式创建!
c. xml里面的命名空间必须要写成dao接口的全路径!
d. xml里面的语句的id值必须是dao接口的方法名字!
e. resultType: 写的是方法的返回值类型,如果方法的返回值是集合类型,那么写的是集合中的元素类型。
f. parameterType : 写的是参数的类型,参数是什么类型,就写什么类型,即是是集合,也写的是集合的类型。
-->
<mapper namespace="com.itheima.dao.UserDao">
<!--在这里写方法对应的sql语句-->
<select id="findAll" resultType="com.itheima.bean.User">
select * from t_user
</select>
</mapper>
创建 SqlMapConfig.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">
<!--
这个文件是mybatis的核心配置文件,它里面包含两部分内容: 连接数据库, 映射文件在哪里-->
<configuration>
<!--
1. 数据库的连接
environments : 用于配置环境,它里面可以定义多个环境,使用 environment来定义
到底是用哪一个环境,取决于 default属性写的是哪个 environment的id值
environment : 用于定义环境,
id :声明一个标识,唯一标识
transactionManager : 事务管理员
dataSource :用不用连接池
-->
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_day01"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--2. 映射文件在哪里?-->
<mappers>
<mapper resource="com/itheima/dao/UserDao.xml"/>
</mappers>
</configuration>
package com.itheima.test;
import com.itheima.bean.User;
import com.itheima.dao.UserDao;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestUserDao {
//查询所有
@Test
public void testFindAll() throws IOException {
//1. 读取SQLMapConfig文件,读取成一个输入流
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 构建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//3. 使用输入流来构建SqlSessionFactory
SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(is);
//4. 问SqlSessionFactory要SqlSession
SqlSession session = sessionFactory.openSession();
//5. 问SqlSession要UserDao的代理对象
UserDao dao = session.getMapper(UserDao.class);
//6. 有了代理对象,就可以调用findAll方法
List<User> list = dao.findAll();
System.out.println("list = " + list);
//7. 关闭SqlSession
session.close();
}
}
掌握Mapper动态代理方式规范
MyBatis进阶【重点】
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
/**
* 添加
* @param user
* @return 影响的行数
*/
int add(User user);
}
<?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.itheima.dao.UserDao">
<!--添加用户: 增删改操作不用写返回的类型,mybatis会自动返回影响的行数-->
<insert id="add" parameterType="com.itheima.bean.User">
insert into t_user values(null , #{username} , #{sex} ,#{birthday} , #{address})
</insert>
</mapper>
package com.itheima.test;
import com.itheima.bean.User;
import com.itheima.dao.UserDao;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class TestUserDao {
@Test
public void testAdd() throws IOException {
//1. 读取SqlMapConfig文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 构建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3. 构建SqlSessionFactory
SqlSessionFactory factory = builder.build(is);
//4. 构建SqlSession
SqlSession session = factory.openSession();
//5. 问SqlSession要dao的代理
UserDao userDao = session.getMapper(UserDao.class);
//6. 调用方法
User user = new User();
user.setUsername("zhangsan");
user.setSex("男");
user.setBirthday(new Date());
user.setAddress("深圳");
userDao.add(user);
//7. 提交事务: mybatis没有默认提交事务 ,它把setAutoCommit(false), 所以对于增删改操作,要记得提交事务
session.commit();
//8. 关闭SqlSession
session.close();
}
}
新增用户 id 的返回值
-
有时候我们存在一些场景,把一条数据存储到数据库了之后,需要马上得到这条记录的id值。因为这条数据是我们自己创建赶出来的,它的id是多少,我们是不知道的,只有这条记录已经存在于数据库里面了,才知道它的id值。
-
什么情况下|场景下,我们需要在插入完这条记录之后,立即就需要得到这条记录的id呢?
当我们往A表里面添加一条记录之后,需要继续往B表里面添加一条记录,但是A表和B表形成了一种主外键关系。B表里面的外键就是A表里面的主键,所以往B表里面添加记录,必须要先知道A表的这条记录的主键
新增用户后, 同时还要返回当前新增用户的 id 值,因为 id 是由数据库的自动增长来实现的,所以就相当于我们要在新增后将自动增长 auto_increment 的值返回。
UserDao.xml
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
/**
* 添加: 获取主键id,使用selectKey去获取
* @param user
* @return
*/
int add02(User user);
}
UserDao.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="com.itheima.dao.UserDao">
<!--添加用户:并且获取id主键返回-->
<insert id="add02" parameterType="com.itheima.bean.User">
insert into t_user values(null , #{username} , #{sex} ,#{birthday} , #{address})
<!--
获取主键 alt + shift + 方向上下 移动快捷键
selectKey: 主要使用来获取主键id
keyProperty : 用参数user里面的什么属性来接收主键值
resultType: 接收主键的属性是什么类型
order: 只能写两个值: before | after
BEFORE: 先执行获取id的操作,再去执行添加数据的操作
AFTER : 先执行添加的操作,再去执行获取id的操作。
SELECT LAST_INSERT_ID() : 获取id的语句
-->
<selectKey keyProperty="uid" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
</mapper>
-
方式二:属性配置
在isnert标签里面直接使用属性keyProperty 和 useGeneratedKeys来设置获取主键id值。
UserDao.java
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
/**
* 添加: 获取主键id,使用属性的写法来获取
* @param user
* @return
*/
int add03(User user);
}
UserDao.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="com.itheima.dao.UserDao">
<!--
添加用户,获取id主键返回
keyProperty :表示,使用参数user对象里面的什么属性来接收主键id
useGeneratedKeys: 表示,是否使用数据库自增创建创建出来的主键id值,还是由mybatis自己搞出来的id值。
true: 使用数据库创建给出来的id值
false: 不是用数据库创建出来的id值,由mybatis给出来。
-->
<insert id="add03" parameterType="com.itheima.bean.User" keyProperty="uid" useGeneratedKeys="true">
insert into t_user values(null , #{username} , #{sex} ,#{birthday} , #{address})
</insert>
</mapper>
新增用户 id 的返回值(字符串类型)
主键不一定是int类型... 主键也可以是字符串类型。只有到以后做项目,有数据库合并,数据库集群的时候。
字符串类型的主键通常就是UUID生成的一串32个字符的字符串。 数据库合并!
package com.itheima.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String myId;
private int uid;
private String username;
private String sex;
private Date birthday;
private String address;
}
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
/**
* 添加: 获取主键id 使用selectKey标签来获取,主键是字符串类型。
* @param user
* @return
*/
int add04(User user);
}
UserDao.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="com.itheima.dao.UserDao">
<!--添加用户,获取id主键返回,但是主键是字符串类型的主键-->
<insert id="add04" parameterType="com.itheima.bean.User" >
insert into t_user values(null , #{username} , #{sex} ,#{birthday} , #{address})
<selectKey keyProperty="myId" resultType="String" order="BEFORE">
select uuid()
</selectKey>
</insert>
</mapper>
修改用户
-
UserDao中添加修改方法
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
//=======================修改=======================
/**
* 根据id来查询用户
* @param id
* @return
*/
User findById(int id);
/**
* 修改用户
* @param user
* @return
*/
int update(User user);
}
<?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.itheima.dao.UserDao">
<!--=========================更新===========================-->
<!--根据id来查询用户-->
<select id="findById" parameterType="int" resultType="com.itheima.bean.User">
select * from t_user where uid = #{id}
</select>
<!--更新用户-->
<update id="update" parameterType="com.itheima.bean.User">
update t_user set username = #{username} , sex = #{sex} , birthday=#{birthday} , address= #{address} where uid = #{uid}
</update>
</mapper>
//修改用户
@Test
public void testUpdate() throws IOException {
//1. 读取SqlMapConfig文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 构建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3. 构建SqlSessionFactory
SqlSessionFactory factory = builder.build(is);
//4. 构建SqlSession
SqlSession session = factory.openSession();
//5. 问SqlSession要dao的代理
UserDao userDao = session.getMapper(UserDao.class);
//6. 调用方法
User user = userDao.findById(7);
//修改数据
user.setUsername("lisi");
userDao.update(user);
//7. 提交事务: mybatis没有默认提交事务 ,它把setAutoCommit(false), 所以对于增删改操作,要记得提交事务
session.commit();
//8. 关闭SqlSession
session.close();
}
删除用户
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
/**
* 删除用户
* @param id
* @return
*/
int delete(int id);
}
-
在 UserDao.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="com.itheima.dao.UserDao">
<!--删除用户-->
<delete id="delete" parameterType="int">
delete from t_user where uid = #{id}
</delete>
</mapper>
添加测试类中的测试方法
//删除用户
@Test
public void testDelete() throws IOException {
//1. 读取SqlMapConfig文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 构建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3. 构建SqlSessionFactory
SqlSessionFactory factory = builder.build(is);
//4. 构建SqlSession
SqlSession session = factory.openSession();
//5. 问SqlSession要dao的代理
UserDao userDao = session.getMapper(UserDao.class);
//6. 调用方法
userDao.delete(7);
//7. 提交事务: mybatis没有默认提交事务 ,它把setAutoCommit(false), 所以对于增删改操作,要记得提交事务
session.commit();
//8. 关闭SqlSession
session.close();
}
模糊查询
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
/**
* 模糊查询,根据姓氏来查询用户
* @param name
* @return
*/
List<User> findByUsername(String name);
}
<?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.itheima.dao.UserDao">
<!--1. 从外面传递进来 % 的写法-->
<select id="findByUsername" parameterType="string" resultType="com.itheima.bean.User">
select * from t_user where username like #{name}
</select>
</mapper>
//模糊查询: 从外面传递进去 %
@Test
public void testFindByUsername() throws IOException {
//1. 读取SqlMapConfig文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 构建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3. 构建SqlSessionFactory
SqlSessionFactory factory = builder.build(is);
//4. 构建SqlSession
SqlSession session = factory.openSession();
//5. 问SqlSession要dao的代理
UserDao userDao = session.getMapper(UserDao.class);
//6. 调用方法
List<User> list = userDao.findByUsername("zhang%");
System.out.println("list = " + list);
//8. 关闭SqlSession
session.close();
}
package com.itheima.dao;
import com.itheima.bean.User;
import java.util.List;
public interface UserDao {
/**
* 模糊查询,根据姓氏来查询用户
* @param name
* @return
*/
List<User> findByUsername02(String name);
/**
* 模糊查询,根据姓氏来查询用户
* @param name
* @return
*/
List<User> findByUsername03(String name);
/**
* 模糊查询,根据姓氏来查询用户
* @param name
* @return
*/
List<User> findByUsername04(String name);
}
<?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.itheima.dao.UserDao">
<!--2. 在语句里面追加 % , 外面不传递进来 % , 使用#{}取参数的值 -->
<select id="findByUsername02" parameterType="string" resultType="com.itheima.bean.User">
select * from t_user where username like #{name}"%"
</select>
<!--3. 在语句里面追加 % , 外面不传递进来 % , 使用${}取参数的值-->
<select id="findByUsername03" parameterType="string" resultType="com.itheima.bean.User">
select * from t_user where username like '${value}%'
</select>
<!--4. 在语句里面追加 % , 外面不传递进来 % , 使用#{}取参数的值-->
<select id="findByUsername04" parameterType="string" resultType="com.itheima.bean.User">
<!--select * from t_user where username like concat("%" , #{name} , "%")-->
select * from t_user where username like concat( #{name} , "%")
</select>
</mapper>
4.小结
-
传递简单类型
#{任意字段}或者${value}
-
传递pojo对象类型
#{javaBean属性名}或者${javaBean属性名}
-
传递的包装的pojo
#{属性名.属性名} 或者${属性名.属性名}
-
传递多个参数
#{别名}