JDBC访问数据库的每个增删改查的操作都需要先创建连接,而且这个连接对象是不能共享的。每个用户每次访问都必须创建一个连接对象,并且这个连接对象应该设置成局部变量。
使用连接池的目的就是为了解决以上2个问题
没有使用连接池的情况:每个用户访问数据库的时候,都是自己创建连接对象
使用连接池的情况:一开始系统启动的时候就创建了一定数量的连接对象,使用的时候直接从连接池中去获取就可以了。不需要自己来创建连接对象。
连接池解决现状问题的原理
Connection连接对象 | 操作特点 |
---|---|
创建时 | 连接对象不再由自己创建,而是系统启动的时候已经创建一定数量的连接, 并且放在连接池中 |
使用时 | 直接从连接池中去获取一个已经创建好的连接对象即可 |
关闭时 | 不是真的关闭连接对象,而是将连接对象再放回到连接池中,供下一个用户使用 |
javax.sql.DataSource接口
实现类在哪?由第三方厂商来实现,只要实现这个接口都可以编写自己的连接池。
DataSource接口中的方法 | 描述 |
---|---|
Connection getConnection() | 从连接池中获取连接对象 |
每个连接池都有很多的参数,几乎所有的参数都是有默认的值的,我们也可以根据实际情况进行调整。参数名在不同的连接池中参数名是有区别的。
常用参数 | 描述 |
---|---|
初始连接数 | 服务器启动的时候创建的连接对象数量 |
最大连接数 | 连接池中最多可以允许放多少个连接对象 |
最长等待时间 | 如果连接池中没有连接对象,设置用户等待的最长时间是多久,单位是毫秒。 如果超过这个时间就抛出异常 |
最长空闲回收时间 | 如果一个连接对象长时间没有人使用,设置多久回收这个对象,默认是不回收。 |
连接池的接口名是什么?
javax.sql.DataSource
从连接池中得到连接的方法是什么?
getConnection()
DataSource本身只是Oracle公司提供的一个接口,没有具体的实现,它的实现由连接池的数据库厂商去实现。我们只需要学习这个工具如何使用即可。
常用的连接池实现组件有这些:
就算不设置任何参数,每个连接池都有默认的配置参数。不同的数据源实现厂商,下面这些属性名会不同。
常用参数 | 描述 |
---|---|
initialPoolSize | 初始连接数,一开始创建多少个连接对象 |
maxPoolSize | 连接池中最大连接数 |
checkoutTimeout | 最长等待时间,等多久没有获取到连接对象,抛出异常 |
maxIdleTime | 最长空闲回收时间,并不是到了这个时间马上回收,看连接池使用情况 |
使用c3p0的配置文件来配置连接池,配置文件有如下几个要求:
C3P0中的类
构造方法 | 描述 |
---|---|
ComboPooledDataSource() | 默认的构造方法,使用默认的配置来创建连接池 |
ComboPooledDataSource(命名配置) | 指定使用哪个命名配置来创建连接池 |
连接同mysql的驱动,复制到lib目录下
配置文件
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day24property>
<property name="user">rootproperty>
<property name="password">rootproperty>
<property name="initialPoolSize">5property>
<property name="maxPoolSize">10property>
<property name="checkoutTimeout">3000property>
default-config>
<named-config name="otherc3p0">
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day23property>
<property name="user">rootproperty>
<property name="password">rootproperty>
<property name="initialPoolSize">3property>
<property name="maxPoolSize">15property>
<property name="checkoutTimeout">2000property>
named-config>
c3p0-config>
Java代码
package com.itheima;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 1. 从连接池中得到10个连接对象,输出每个对象
* 2. 分别设置默认配置和命名配置
*/
public class Demo1C3p0 {
public static void main(String[] args) throws SQLException {
//1.创建连接池,使用默认配置。ComboPooledDataSource实现了DataSource接口
//ComboPooledDataSource ds = new ComboPooledDataSource();
//使用命名配置
ComboPooledDataSource ds = new ComboPooledDataSource("otherc3p0");
//2.使用连接池,从连接池中获取10个连接对象
for (int i = 1; i <= 16; i++) {
Connection connection = ds.getConnection();
System.out.println("第" + i + "个连接对象:" + connection);
//第5个释放
if (i==5) {
//放回到连接池中
connection.close();
}
}
}
}
c3p0有哪两种配置方案?
在Java中如何使用上面的2种配置?
使用druid创建连接池,并且从连接池中得到连接对象
Druid是阿里巴巴开发的号称为监控而生的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票。
Druid的下载地址:https://github.com/alibaba/druid
DRUID连接池使用的jar包:druid-1.0.9.jar
参数 | 说明 |
---|---|
url | 连接字符串 |
username | 用户名 |
password | 密码 |
driverClassName | 驱动类名,会自动根据URL识别,这一项可以不配置 |
initialSize | 初始连接数 |
maxActive | 最大连接数 |
maxWait | 最长等待时间 |
Class类中的方法 | 说明 |
---|---|
InputStream getResourceAsStream(String path) | 加载类路径下配置文件,转成一个输入流对象 |
DruidDataSourceFactory的方法 | 方法 |
---|---|
public static DataSource createDataSource(Properties properties) | 通过属性集合中属性,创建一个连接池 |
使用druid创建连接池,从连接池中得到连接对象,输出得到的连接对象。
在src目录下创建一个properties文件,文件名随意,设置上面的参数
Java代码
在src目录下新建一个DRUID配置文件,命名为:druid.properties
url=jdbc:mysql://localhost:3306/test
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=2000
Java代码
package com.itheima;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
public class Demo2Druid {
public static void main(String[] args) throws Exception {
//1.从类路径下加载配置文件,获取一个输入流。如果不指定路径,默认是读取同一个包下资源文件
InputStream inputStream = Demo2Druid.class.getResourceAsStream("/druid.properties");
//2.使用Properties对象的方法将配置文件中属性加载到Properties对象中
Properties properties = new Properties();
//加载了配置文件中所有的属性
properties.load(inputStream);
//3.通过druid的工厂类创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//获取10个连接对象
for (int i = 1; i <= 11; i++) {
Connection connection = dataSource.getConnection();
System.out.println("第" + i + "个连接对象:" + connection);
//第3个连接关闭
if (i==3) {
connection.close();
}
}
}
}
Class类中的方法 | 说明 |
---|---|
InputStream getResourceAsStream(String path) | 读取类路径下配置文件,返回一个字节输入流对象 |
DruidDataSourceFactory的方法 | 方法 |
---|---|
public static DataSource createDataSource(Properties properties) | 通过数据库的属性集合,创建一个连接池对象 |
使用连接池的方式获取连接对象改造JDBC的工具类
使用Druid连接池来获取连接对象,达到提升访问数据库速度目的
工具类代码
package com.itheima.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.itheima.Demo2Druid;
import javax.sql.DataSource;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* 访问数据库的工具类
* @author newboy
* @version 3.0
*/
public class JdbcUtils {
//声明一个连接池对象,使用druid连接池
private static DataSource dataSource;
//在类加载的时候就创建连接池
static {
//1.从类路径下加载配置文件,获取一个输入流。如果不指定路径,默认是读取同一个包下资源文件
try (InputStream inputStream = JdbcUtils.class.getResourceAsStream("/druid.properties")) {
//2.使用Properties对象的方法将配置文件中属性加载到Properties对象中
Properties properties = new Properties();
//加载了配置文件中所有的属性
properties.load(inputStream);
//3.通过druid的工厂类创建连接池
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池对象
* @return
*/
public static DataSource getDataSource() {
return dataSource;
}
/**
* 得到数据库的连接
*/
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**
* 关闭连接
* 查询调用这个方法
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭连接
* 增删改没有结果集
*/
public static void close(Connection connection, Statement statement) {
//直接调用上面的方法
close(connection, statement, null);
}
/**
* 通用的增删改的方法
* @param sql 要执行的SQL语句
* @param params 替换占位符的真实地址,在方法内部是一个数组(从0开始)
* @return 影响的行数
*/
public static int update(String sql, Object... params) {
Connection connection = null;
PreparedStatement ps = null;
int row = 0; //影响的行数
try {
//1.创建连接对象
connection = getConnection();
//2.创建预编译的语句对象
ps = connection.prepareStatement(sql);
//2.5 得到参数元数据
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount(); //获取有几个参数
//3.替换占位符为真实的值
for (int i = 0; i < count; i++) {
ps.setObject(i + 1, params[i]); //一定是个对象类型
}
//4.执行SQL语句
row = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(connection, ps); //调用自己的方法
}
return row;
}
/**
* 通用的查询方法
* @param sql 要执行的SQL语句
* @param clazz 要实例化的类型,如:Student.class, Employee.class
* @param params 替换占位符的真实值
* @return 封装好的集合对象
*/
public static <T> List<T> query(String sql, Class<T> clazz, Object... params) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
List<T> list = new ArrayList<>();
try {
//1.创建连接对象
connection = getConnection();
//2. 创建预编译的语句对象
ps = connection.prepareStatement(sql);
//2.5 得到参数元数据
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount(); //获取有几个参数
//3.替换占位符为真实的值
for (int i = 0; i < count; i++) {
ps.setObject(i + 1, params[i]); //一定是个对象类型
}
//4.执行查询操作
resultSet = ps.executeQuery();
//5.遍历整个结果集,封装到集合中,每个元素是一个对象
while (resultSet.next()) {
//每条记录封装成一个对象,创建一个对象
T obj = clazz.getConstructor().newInstance();
//先得到实体类中有哪些属性
Field[] fields = clazz.getDeclaredFields(); //得到所有成员变量,包含私有的
//遍历每个成员变量,进行赋值
for (Field field : fields) {
//私有的要暴力
field.setAccessible(true);
//列名=属性名
String name = field.getName();
//要赋值的对象,值
field.set(obj, resultSet.getObject(name)); //从结果集中获取数据
}
//6.添加到集合中
list.add(obj);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
close(connection, ps, resultSet);
}
return list;
}
}
使用工具类
package com.itheima;
import com.itheima.entity.Student;
import com.itheima.utils.JdbcUtils;
import java.util.List;
/**
* 使用工具类
*/
public class Demo3UseUtils {
public static void main(String[] args) {
//使用工具类添加1条记录
int row = JdbcUtils.update("insert into student values(null,?,?,?)", "嫦娥", 0, "1997-07-07");
System.out.println("添加了" + row + "条");
//使用工具类查询所有的数据
List<Student> students = JdbcUtils.query("select * from student", Student.class);
//打印
students.forEach(System.out::println);
}
}
修改了连接的获取方式
什么是框架
框架解决了哪些问题
分层开发下常见的框架
程序开发中的框架往往是对常见功能的封装,程序框架理解为基础或者机械标准件(例如螺丝螺母标准的机械部件)。
假如你要造一辆马车,在没有框架的情况下,你需要自己去伐木,去把木头做成木板,木棍,然后组成轮子,门,等部件,然后组装起来。
但如果你用了框架,就相当于你有现成的轮子,门等部件,你只需要组装一下就可以了。
一个框架是一组可复用的设计构件。
框架(Framework)是整个或者部分系统的可重用设计,是JavaEE底层技术的封装。
框架是可以被开发者定制的应用骨架。
在JavaEE体系中,有着各种各样的技术。不同的软件企业,根据自身的业务需求选择不同的技术,容易造成应用依赖技术,增加了项目开发实现的复杂性和技术风险性。企业项目中应该将应用的设计与实现技术解耦。
企业项目中使用框架,只需要专注实现业务需求。使用框架的方便性,提升了开发效率。
一个成熟的框架,经过了在众多企业项目中的验证使用,稳定性有保障。
mybatis框架介绍
mybatis框架官网和jar包下载
mybatis是Apache软件基金会下的一个开源项目,前身是iBatis框架。2010年这个项目由apache 软件基金会迁移到google code下,改名为mybatis。2013年11月又迁移到了github。
ORM概念:Object Relational Mapping 对应关系映射
java语言是面向对象的,mysql是关系型数据库,两者需要有一个映射机制,这个映射机制称为ORM。由框架自动去完成的。
mybatis的两种映射方式
网址:http://www.mybatis.org/mybatis-3/
连接到github地址:https://github.com/mybatis/mybatis-3/releases
mybatis框架一个什么框架?
什么是ORM?
将关系型数据库映射成面向对象编程的语言,如果使用框架,这由框架去完成
mybatis框架可以使用哪两种配置,实现java对象与sql语句的对象关系映射?
mybatis开发环境的搭建
了解mybatis的开发步骤
利用mybatis框架,从MySQL中查询所有的用户
要查询的表
create table user (
id int primary key auto_increment,
username varchar(20) not null,
birthday date,
sex char(1) default '男',
address varchar(50)
);
insert into user values (null, '孙悟空','1980-10-24','男','花果山水帘洞');
insert into user values (null, '白骨精','1992-11-12','女','白虎岭白骨洞');
insert into user values (null, '猪八戒','1983-05-20','男','福临山云栈洞');
insert into user values (null, '蜘蛛精','1995-03-22','女','盤丝洞');
select * from user;
创建项目(模块)
创建lib目录,复制以下包
核心配置文件:sqlMapConfig.xml放在src目录下
日志记录的配置文件:log4j.properties
编写用户DAO接口(UserMapper)
编写用户DAO接口映射文件(UserMapper.xml)放在与DAO接口相同目录
修改核心配置文件中sqlMapConfig.xml,加载UserMapper.xml
编写测试代码
mybatis配置文件分两种,这2种都是XML文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oiVhV4Po-1592716080793)(assets/1562393860427.png)]
复制到src目录下,与sqlMapConfig.xml核心配置文件在一起
作用:mybatis的日志记录组件的配置文件
包装类:
package com.itheima.entity;
import java.sql.Date;
/**
用户实体类对象 */
public class User {
//基本类型使用的是包装类,使用包装类的好处,更加与表吻合,如:表中这列为NULL,int默认是0,Integer没有就是null
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
public User() {
}
public User(Integer id, String username, Date birthday, String sex, String address) {
this.id = id;
this.username = username;
this.birthday = birthday;
this.sex = sex;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
在src目录下核心配置文件
可以在mybatis官网入门中找到配置模板:
http://www.mybatis.org/mybatis-3/zh/getting-started.html
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/day24"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/itheima/dao/UserMapper.xml"/>
mappers>
configuration>
就是DAO接口中的方法:
package com.itheima.dao;
import com.itheima.entity.User;
import java.util.List;
/**
* DAO层的接口
*/
public interface UserMapper {
/**
* 查询所有的用户
*/
List<User> findAllUsers();
}
作用:
创建com/itheima/dao,在目录中创建UserMapper.xml映射文件
映射文件的模板可以在http://www.mybatis.org/mybatis-3/zh/getting-started.html找到。
<mapper namespace="com.itheima.dao.UserMapper">
<select id="findAllUsers" resultType="com.itheima.entity.User">
select * from user
select>
mapper>
添加UserMapper.xml的映射
<mappers>
<mapper resource="com/itheima/dao/UserMapper.xml"/>
mappers>
属性名 | 作用 |
---|---|
namespace | 指定接口的完全限定名 |
id | 接口中方法的名字 |
resultType | 指定方法返回的数据类型,如果是集合类型,指定集合中每个元素的类型 |
标签体 | SQL语句 |
通过框架提供的Resources类,加载sqlMapConfig.xml,得到文件输入流InputStream对象
实例化会话工厂创建类SqlSessionFactoryBuilder
通过上面的SqlSessionFactoryBuilder对象,读取核心配置文件的输入流,得到会话工厂SqlSessionFactory类
使用SqlSessionFactory对象,创建SqlSession对象
获取接口的对象UserMapper,得到接口的代理对象
执行数据库的查询操作,输出用户信息
关闭会话,释放资源。
package com.itheima.test;
import com.itheima.dao.UserMapper;
import com.itheima.entity.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* 编写测试类
*/
public class TestUserMapper {
public static void main(String[] args) throws IOException {
//0.mybatis也提供了一个工具类,直接读取src目录下配置文件,转成一个输入流。注:不要导错包
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//1.创建会话工厂建造类
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//2.创建会话工厂对象,读取核心配置文件
SqlSessionFactory factory = builder.build(inputStream);
//3.从工厂中获取会话对象,相当于获取数据库的连接对象
SqlSession sqlSession = factory.openSession();
//4.获取DAO的代理对象,它是DAO的实现类。多态,前面是接口,后面是它的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//5.调用DAO接口中方法来实现增删改查
List<User> users = userMapper.findAllUsers();
//6.输出查询的结果
users.forEach(System.out::println);
//7.释放资源
sqlSession.close();
}
}
注:UserMapper的对象是由mybatis生成的接口代理对象
连接对象 | 操作特点 |
---|---|
创建时 | 由连接池一开始就创建一定数量的连接对象,不需要自己创建连接对象 |
使用时 | 从连接池中直接获取一个创建好的连接对象 |
关闭时 | 不是真的关闭连接对象,而是放回到连接池中 |
能够使用C3P0连接池
能够使用DRUID连接池
掌握mybatis框架开发快速入门