文章目录
原生JDBC访问数据库
- JDBC是SUN公司提出的一些列
规范
:而具体的实现是由各个数据库厂商来实现的:因为各个数据库都有其特殊性:所以规范是没有办法确定其实现的:JDBC是一种桥接模式
- JDBC操作数据库的步骤:
(1) 注册数据库驱动
(2) 获取数据库连接对象
(3) 准备SQL语句
(4) 获取执行SQL语句的Statement对象
(5) 执行SQL语句
(6) 处理结果
(7) 关闭相关资源
// 1. 注释数据库驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// 2. 获取数据库连接对象:Connection对象
String url = "jdbc:mysql://localhost:3306/clight";
String username = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url, username, password);
// 3. 准备SQL语句
String sql = "select * from t_user";
// 4. 得到指向SQL语句的Statement对象
PreparedStatement pstmt = connection.prepareStatement(sql);
// 5. 发送SQL语句给数据库服务器
ResultSet rs = pstmt.executeQuery();
// 6. 处理结果
while (rs.next()) {
String id = rs.getString("id");
String name = rs.getString("username");
String pwd = rs.getString("password");
System.out.println(id + ": " + name + "--" + pwd);
System.out.println("--------------------------------");
}
// 7. 关闭资源
if (rs != null) {
rs.close();
}
if (pstmt != null) {
pstmt.close();
}
if (connection != null) {
connection.close();
}
- 数据库:采用mysql
原生JDBC访问数据库存在的问题
- 数据库连接,使用时就创建,不使用立即释放:对数据库进行频繁连接开启与关闭,造成数据库资源浪费,影响数据库性能:
解决方案
:使用数据库连接池管理数据库连接 - 将SQL语句
硬编码
到Java代码中,如果将来SQL语句需要修改,那么需要重新编译Java代码:不利于系统维护!解决方案
:将SQL语句配置在XML配置文件中,即使SQL语句发生变化,也不需要对Java代码进行重新编译 - 向PreparedStatement中设置参数,对占位符位置和设置参数值,
硬编码
在Java代码中,不利于系统维护!解决方案
:将SQL语句以及占位符和参数全部配置在XML文件中 - 从ResultSet中遍历结果集数据时,存在
硬编码
:获取表的字段进行硬编码,不利于系统维护!解决方案
:将查询的结果集自动映射为Java对象
ORM模型
- ORM:Object Realational Mapping:对象关系映射:数据库表中的记录与简单Java对象POJO(
Plain Ordinary Java Object
)的映射关系模型 - 对象模型:Java代码中的实体类对象
- 关系模型:数据表中的记录
- 所有的ORM模型都是对底层JDBC的封装:不同的ORM模型对JDBC的封装程度不一样
MyBatis框架原理
搭建MyBatis开发环境
-
第一步
:创建项目:JavaSE项目或JavaEE项目均可:因为MyBatis是持久层框架
-
第二步
:导 包
(1)MyBatis核心包
(2)MyBatis依赖包
(3)数据库驱动包
-
第三步
:创建实体类
package com.ycom.mybatis.entity;
import java.util.Date;
public class User {
// 实体类属性要求: 属性与数据表的字段对应
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
第四步
:编写映射配置文件:映射配置文件的位置与名称没有固定要求,但是映射配置文件的名称一般都是有约定的:基于MyBatis原生的DAO开发模式下的映射配置文件的名称为:实体类名称.xml,例如User.xml;基于Mapper代码的开发模式下的映射文件的名称为:实体类名+Mapper.xml,例如UserMapper.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属性: 对SQL进行分类化管理
(1) MyBatis原生DAO开发模式: 可以随便起名称
(2) 基于Mapper代理开发模式: namespace属性的值是有要求的: 有特殊重要的作用
-->
<mapper namespace="test">
<!--
id属性:用来标识映射配置文件中的SQL语句
将SQL语句封装到MappedStatement对象中:称之为statement的id
parameterType属性:指定输入参数类型,这里指定int类型
resultType属性:指定SQL输出结果的所映射的Java实体类对象的类型
select指定resultType表示将单条记录映射成Java对象
#{}表示站位符
#{id}中的id表示接收输入的参数,参数名称就是id
如果输入参数类型是基本类型,那么#{}中的参数可以是任意的,可以是value或者其他
-->
<select id="findUserById" parameterType="int" resultType="com.ycom.mybatis.entity.User">
select * from user where id=#{value}
</select>
</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>
<!-- 第一部分: 配置数据源 -->
<!-- 与Spring整合后 environments配置都将废除-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" /><!-- 使用JDBC事务管理: 事务控制由MyBatis管理-->
<dataSource type="POOLED"><!-- 数据库连接池: 由MyBatis管理 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 第二部分:加载映射配置文件 -->
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
</configuration>
第六步
: 编写Java代码
package com.ycom.mybatis.test;
import java.io.InputStream;
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 com.ycom.mybatis.entity.User;
public class MyBatisTest {
@Test
public void testQuery() throws Exception {
// 1. 创建会话工厂: 传入全局配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 得到会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 通过sqlSession操作数据库
User user = sqlSession.selectOne("test.findUserById", 1);
System.out.println(user);
// 释放资源
sqlSession.close();
}
}
第七步
:配置日志:在src模式下配置log4j.properties
# Global logging configuration
# 开发环境使用DEBUG,生产环境使用info
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
MyBatis入门案例
- 案例描述:对用户表进行CRUD操作
(1)根据用户id(主键)查询用户信息
(2)根据用户名称进行模糊查询用户信息
(3)添加用户
(4)删除用户
(5)更新用户
项目的目录结构
根据用户id查询用户信息
第一步
:导包:MyBatis核心包与依赖包、数据库驱动包第二步
:配置日志文件:db.properties第三步
:配置全局配置文件:SqlMapConfig.xml:配置数据源第四步
:创建实体类:User.java第五步
:配置映射配置文件:User.xml第六步
:配置全局配置文件:加载映射配置文件第七步
:写代码:实现根据用户id查询用户信息
- 采用Debug模式调试代码,发现:SqlSession中的autoCommit=false,如果是进行数据库的更新操作,那么必须手动的进行commit操作
根据用户名模糊查询用户信息
-
第一步
:导包:MyBatis核心包与依赖包、数据库驱动包
-
第二步
:配置日志文件:db.properties
-
第三步
:配置全局配置文件:SqlMapConfig.xml:配置数据源
-
第四步
:创建实体类:User.java
-
第五步
:配置映射配置文件:User.xml
-
第六步
:配置全局配置文件:加载映射配置文件
-
第七步
:写代码:根据用户名模糊查询用户信息
-
第八步
:查看结果
添加用户
一般的添加用户的过程
- 映射配置文件
- 程序代码
- 注意:占位符#{}写的是实体类的属性名,不是成员变量名:但是正常情况下属性名就是成员变量名
添加用户后获取自增长的主键值
- 需求:添加用户的时候主键是自增长的,那么在添加用户之后能否获取新增的主键?
- 获取主键的作用:在进行表关联操作的时候,在插入主表之后需要插入子表的操作,子表的外键值就是主表的主键值,此时就要获取主表的主键值:那么此时就需要自增主键的返回:主表的主键是子表的外键
(1)MySQL自增长主键,执行insert提交之前自动生成一个自增长主键
(2)通过MySQL函数获取刚插入记录的自增长主键:LAST_INSERT_ID()
(3)LAST_INSERT_ID()
这个函数必须是在insert之后调用 - 映射配置文件中的配置:
<!-- 3. 添加用户 -->
<insert id="addUser"
parameterType="com.ycom.mybatis.entity.User">
<!--
将插入的记录的主键返回:返回到User对象中
SELECT LAST_INSERT_ID(): 得到刚刚insert操作的主键值: 注意:只适用于自增长主键
keyProperty:将查询到的主键值设置到parameterType指定类型对象的哪个属性
order:SELECT LAST_INSERT_ID()相对于insert语句的执行顺序
resultType:指定SELECT LAST_INSERT_ID()结果的类型
-->
<selectKey keyProperty="uid" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
insert into user(username, address) values(#{name}, #{address})
</insert>
- 代码实现:
@Test
public void testAddUser2() throws IOException {
// 1. 创建会话工厂: 传入全局配置文件
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
// 2. 得到会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 通过sqlSession操作数据库
User user = new User();
user.setUsername("Admin");
user.setAddress("China");
sqlSession.insert("user.addUser", user);
System.out.println(user.getUid());
sqlSession.commit();
sqlSession.close();
}
//===========================================================================
// DEBUG [main] - ==> Preparing: insert into user(username, address) values(?, ?)
// DEBUG [main] - ==> Parameters: Admin(String), China(String)
// DEBUG [main] - <== Updates: 1
// DEBUG [main] - ==> Preparing: SELECT LAST_INSERT_ID()
// DEBUG [main] - ==> Parameters:
// DEBUG [main] - <== Total: 1
// 32
添加用户后获取uuid的主键值
- 使用uuid作为主键前提必须修改主键的类型为字符串
执行思路
:
(1) 先通过MySQL的函数uuid()查询到主键,其实就是生成一个uuid的主键:SELECT UUID()
(2) 将拿到的uuid作为insert操作的主键值给赋值进去:必须在数据库客户端完成,因为MySQL是不能在插入记录的时候自动生成uuid()的
(3) 结论:SELECT UUID()
必须是在INSERT语句之前比执行,得到结果作为INSERT语句中的VALUES值- 创建一张表:t_user
CREATE TABLE t_user(
id VARCHAR(50) PRIMARY KEY,
username VARCHAR(50),
age INT
)CHARSET utf8;
- 创建一个实体类:User
package com.ycom.mybatis.entity;
public class User2 {
private String id;
private String username;
private int age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- 编写映射配置文件
<insert id="addUser2" parameterType="com.ycom.mybatis.entity.User2">
<!-- 将插入记录的主键返回
order="BEFORE":在执行insert之前执行select uuid()这条SQL语句,得到主键值
resultType:select uuid()结果的类型
keyProperty:实体类对应的属性名
-->
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
select uuid()
</selectKey>
insert into t_user values(#{id}, #{username}, #{age})
</insert>
- 编写测试代码
删除用户
-
映射配置文件
-
程序代码
更新用户
- 映射配置文件
- 程序代码
总结
-
parameterType
:在映射配置文件中指定输入参数类型:基本类型或引用类型:该属性可以省略,例如没有参数 -
resultType
:在映射配置文件中指定输出结果的类型:该属性可以省略 -
#{}
:表示一个占位符,#{}接收输入参数,类型可以是基本类型、pojo、hashmap
(1) 类型如果是基本类型:#{}可以写成value或其他值:建议一律写成value
(2) 类型如果是pojo类型:通过OGNL读取对象中属性值
,通过属性.属性.属性…方式获取对象属性值 -
${}
:表示一个拼接符号,会引入SQL注入,所以不建议使用
(可以使用#{}占位符,在Java代码给出模式匹配的语句)!
—${value}
表示接收输入参数的内容,其类型可以是基本类型、pojo、hanshmap:
(1) 类型如果是基本类型:${value}中只能写 value
,即只能是${value}
(2) 类型如果是pojo类型:通过OGNL读取对象中属性值
,通过属性.属性.属性…方式获取对象属性值 -
SqlSession#selectOne()
:表示查询出一条记录进行映射:如果用selectOne可以实现的那么也可以使用selectList来实现:list中只有一个对象而已 -
SqlSession#selectList()
:表示查询出多条记录进行映射成一个列表:如果使用selectList查询多条记录则不能使用selectOne来替代:否则会抛出异常
MyBatis与Hibernate本质区别与应用场景
- Hibernate是一个标准的ORM框架:对象关系映射框架:入门门槛较高、不需要编写SQL语句,SQL语句就自动生成了:但是对SQL语句进行优化、修改比较困难
Hibernate应用场景
:适用于需求变化不多的中小型项目,比如:后台管理系统、ERP、CRM、OA- MyBatis专注于SQL本身,需要程序猿自己编写SQL语句:SQL优化、修改比较方便
- MyBatis是一个不完全的ORM框架:虽然程序猿需要自己写SQL,MyBatis也可以实现映射:输入映射与输出映射
MyBatis应用场景
:适用于需求变化较多的项目,比如:互联网项目企业进行技术选型
:以低成本、高回报作为技术选型的原则,根据项目组的技术力量进行选择
SqlSession使用范围
SqlSessionFactoryBuilder
- 通过SqlSessionFactoryBuilder来创建会话工厂SqlSessionFactory
- 将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder
- 在需要创建SqlSessionFactory对象时,只需要new一个SqlSessionFactoryBuilder即可
SqlSessionFactory
- 通过SqlSessionFactory创建SqlSession,使用单例模式管理SqlSessionFactory:工厂一旦创建就使用有一个实例
- 将来MyBatis和Spring整合后,使用单例模式管理SqlSessionFactory
- SqlSessionFactory是单例模式,SqlSessionFactory是由SqlSessionFactoryBuilder创建的,那么也要求SqlSessionFactoryBuilder是单例模式吗?
不要求
:将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理:在需要创建SqlSessionFactory对象时,只需要new一个SqlSessionFactoryBuilder即可:因为SqlSessionFactory已经是单例管理了,那么SqlSessionFactoryBuilder只会new一次
package com.ycom.mybatis.util;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisUtils {
private static final SqlSessionFactory sqlSessionFactory;
static {
String resource = "SqlMapConfig.xml";// 全局配置文件放在src目录下
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
SqlSession
- SqlSession是面向用户(程序猿)的接口
- SqlSession提供了很多用于操作数据库的方法:例如selectOne(返回单个对象)与selectList(返回单个或多个对象)
- SqlSession是线程不安全的:SqlSession的实现类中除了接口中操作数据库的方法之外,还有
数据域的属性
,因为这些东西的存在就会导致线程不安全
- SqlSession中操作数据库的方法会修改数据域的属性值
SqlSession最佳的应用场合
:方法体内:作为局部对象使用- 只要是线程不安全的对象都只能放在方法体内:局部对象是安全的
MyBatis的配置文件
- MyBatis中的配置文件有两个:全局配置文件 与 映射配置文件:全局配置文件负责加载映射配置文件
- 全局配置文件:全局配置文件的名称与位置没有固定要求:但是一般在开发中全局配置文件是直接放在src根目录下;全局配置文件的名称一般为:SqlMapConfig.xml
- 映射配置文件:映射配置文件的名称与位置没有固定要求:但是根据不同的开发模式,映射配置文件的位置与名称有不同的约定规则:
(1) 基于原生DAO开发模式:映射配置文件的名称一般为:实体类名称.xml,例如User.xml;映射配置文件的路径一般是在src目录下或src目录的子目录下
(2) 基于Mapper代理开发模式:映射配置文件的名称一般为:实体类名称+Mapper.xml:例如UserMapper…xml;映射配置文件的路径与代理接口在同一目录下