jdbc基本操作
jdbc操作顺序
导入驱动包,或者引入依赖
1.装载驱动
Class.forName("com.mysql.jdbc.Driver");
2.获取连接对象 connection
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/zhongfucheng", "root", "1234");
3.获取执行SQL语句的对象 statement
Statement statement = connection.createStatement();
4.执行SQL语句 得到结果集 resultSet
ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
//操作结果集
//遍历结果集,得到数据
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
System.out.println(resultSet.getString(2));
}
####
结果集的列 索引从1开始
- 关闭连接
resultSet.close();
statement.close();
connection.close();
JDBC改进
preparedStatment
PreparedStatement对象继承Statement对象,它比Statement对象更强大,使用起来更简单
- PreparedStatement可以使用占位符,简化sql的编写
- PreparedStatement可对SQL进行预编译,提高效率,预编译的SQL存储在PreparedStatement对象中
//模拟查询id为2的信息
String id = "2";
Connection connection = UtilsDemo.getConnection();
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement preparedStatement = connection.preparedStatement(sql);
//第一个参数表示第几个占位符【也就是?号】,第二个参数表示值是多少
preparedStatement.setString(1,id);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
//释放资源
UtilsDemo.release(connection, preparedStatement, resultSet);
元数据
元数据其实就是数据库,表,列的定义信息
通过元数据可以获得数据库表的列名
- ParameterMetaData --参数的元数据
- ResultSetMetaData --结果集的元数据
- DataBaseMetaData --数据库的元数据
案例:将查询结果封装到bean对象中
//接口实现类,对结果集封装成一个Bean对象
public class BeanHandler implements ResultSetHandler {
//要封装成一个Bean对象,首先要知道Bean是什么,这个也是调用者传递进来的。
private Class clazz;
public BeanHandler(Class clazz) {
this.clazz = clazz;
}
@Override
public Object hanlder(ResultSet resultSet) {
try {
//创建传进对象的实例化 反射
Object bean = clazz.newInstance();
if (resultSet.next()) {
//拿到结果集元数据
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
for (int i = 0; i < resultSetMetaData.getColumnCount(); i++) {
//获取到每列的列名 从1开始
String columnName = resultSetMetaData.getColumnName(i+1);
//获取到每列的数据 从1开始
String columnData = resultSet.getString(i+1);
//设置Bean属性
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(bean,columnData);
}
//返回Bean对象
return bean;
}
连接池
自己编写连接池
-
实现java.sql.DataSource接口
-
创建容器用来保存Connection【此容器选择LinkedList】
-
静态代码块中读取连接信息
static { //读取文件配置 InputStream inputStream = Demo1.class.getClassLoader().getResourceAsStream("db.properties"); Properties properties = new Properties(); try { properties.load(inputStream); String url = properties.getProperty("url"); String username = properties.getProperty("username"); String driver = properties.getProperty("driver"); String password = properties.getProperty("password"); //加载驱动 Class.forName(driver); //获取多个连接,保存在LinkedList集合中 for (int i = 0; i < 10; i++) { Connection connection = DriverManager.getConnection(url, username, password); list.add(connection); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } }
-
实现DataSource中的getConnection()方法
-
在getConnection()中返回一个动态代理对象,用来改写close()方法
@Override public Connection getConnection() throws SQLException { if (list.size() > 0) { final Connection connection = list.removeFirst(); //看看池的大小 System.out.println(list.size()); //返回一个动态代理对象 return (Connection) Proxy.newProxyInstance(Demo1.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果不是调用close方法,就按照正常的来调用 if (!method.getName().equals("close")) { return method.invoke(connection, args); } else { //进到这里来,说明调用的是close方法 list.add(connection); //再看看池的大小 System.out.println(list.size()); } return null; } }); } return null; }
连接与线程绑定
/*
* 连接工具类,用于从数据源中获取连接,并且实现和线程的绑定
* */
@Component("connectionUtils")
public class ConnectionUtils {
private ThreadLocal<Connection> sql=new ThreadLocal<Connection>();
//此处连接池,使用c3p0
@Autowired
private DataSource datasource;
public void setDatasource(DataSource datasource) {
this.datasource = datasource;
}
//获取当前线程的连接
public Connection getThreadConnection(){
try {
Connection con = sql.get();
if (con == null) {
//当前线程没有连接,从数据源中获取连接
con = datasource.getConnection();
//把con绑定线程ThreadLocal
sql.set(con);
}
return con;
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void removeConnection(){
//线程和连接解绑,因为连接的close方法不是将连接删除而是还回线程池,所以将线程和连接解绑
sql.remove();
}
}
这样可以直接从线程中获取连接
#ThreadLocal的作用:
简要言之:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal能够实现当前线程的操作都是用同一个Connection,保证了事务!
连接池C3p0的使用
以下为在spring框架中的使用
-
导入依赖
<dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency>
-
配置连接池
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <!--jdbc:mysql://localhost:3306/数据库名--> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property> </bean>
-
注入datasource,获取连接
@Autowired private DataSource datasource; Connetion con = datasource.getConnection();
Dbutils
dbutils它是对JDBC的简单封装,极大简化jdbc编码的工作量
一般配合c3p0使用
DbUtils类
用来连接的工具类,一般不使用,用c3p0代替
QueryRunner类
该类简化了SQL查询,配合ResultSetHandler使用,可以完成大部分的数据库操作,重载了许多的查询,更新,批处理方法。大大减少了代码量
ResultSetHandler接口
该接口规范了对ResultSet的操作,要对结果集进行什么操作,传入ResultSetHandler接口的实现类即可。
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- ScalarHandler 将ResultSet的一个列到一个对象中。
@org.junit.Test
public void query()throws SQLException {
//创建出QueryRunner对象
QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "SELECT * FROM student";
List list = (List) queryRunner.query(sql, new BeanListHandler(Student.class));
System.out.println(list.size());
}
c3p0配置之后的bean就是一个DateSource对象
或者:
将connection对象传入
runner.query(connectionUtils.getThreadConnection(),"select* from account ", new BeanListHandler<Account>(Account.class));
具体使用过程请看day5项目
Mysql分页
mysql查询分页的语法:
/*
Mysql分页语法:
@start---偏移量,不设置就是从0开始【也就是(currentPage-1)*lineSize】
@length---长度,取多少行数据
*/
SELECT *
FROM 表名
LIMIT [START], length;
分页查询需要的参数
- currentPage 为当前页【由用户决定的】
- lineSize 每页的数据条数【由我们开发人员决定
- totalRecord–总数据数【查询表可知】
- pageCount–页数【totalRecord和lineSize决定】
配合图片查看我们的需求:
-
算出多少页数据
用totalRecord除以lineSize
-
根据页码,从数据库中查出相应数据
传入currentPage 和lineSize
Mysql分页具体代码:(原生方式)
//每页显示3条数据
int lineSize = 3;
//总记录数
int totalRecord = getTotalRecord();
//假设用户指定的是第2页
int currentPage = 2;
//一共有多少页
int pageCount = getPageCount(totalRecord, lineSize);
//使用什么数据库进行分页,记得要在JdbcUtils中改配置
List<Person> list = getPageData2(currentPage, lineSize);
for (Person person : list) {
System.out.println(person);
}
}
//使用JDBC连接Mysql数据库实现分页
public static List<Person> getPageData(int currentPage, int lineSize) throws SQLException {
//从哪个位置开始取数据
int start = (currentPage - 1) * lineSize;
QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "SELECT name,address FROM person LIMIT ?,?";
List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{start, lineSize});
return persons;
}
public static int getPageCount(int totalRecord, int lineSize) {
//简单算法
//return (totalRecord - 1) / lineSize + 1;
//此算法比较好理解,把数据代代进去就知道了。
return totalRecord % lineSize == 0 ? (totalRecord / lineSize) : (totalRecord / lineSize) + 1;
}
public static int getTotalRecord() throws SQLException {
//使用DbUtils框架查询数据库表中有多少条数据
QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "SELECT COUNT(*) FROM person";
Object o = queryRunner.query(sql, new ScalarHandler());
String ss = o.toString();
int s = Integer.parseInt(ss);
return s;
}
Orcale中的分页:(使用Orcale支持的SQL语句)
//使用JDBC连接Oracle数据库实现分页
public static List<Person> getPageData2(int currentPage, int lineSize) throws SQLException {
//从哪个位置开始取数据
int start = (currentPage - 1) * lineSize;
//读取前N条数据
int end = currentPage * lineSize;
QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
String sql = "SELECT " +
" name, " +
" address " +
"FROM ( " +
" SELECT " +
" name, " +
" address , " +
" ROWNUM rn " +
" FROM person " +
" WHERE ROWNUM <= ? " +
")temp WHERE temp.rn>?";
List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{end, start});
return persons;
}
" address " +
"FROM ( " +
" SELECT " +
" name, " +
" address , " +
" ROWNUM rn " +
" FROM person " +
" WHERE ROWNUM <= ? " +
")temp WHERE temp.rn>?";
List<Person> persons = (List<Person>) queryRunner.query(sql, new BeanListHandler(Person.class), new Object[]{end, start});
return persons;
}