【Java Se】JDBC
启停服务
net start mysql
net stop mysql
登录
mysql -u -p
访问指定IP的mysql
mysql -u root -P 3306 -h localhost -pabc123
-p后面不能加空格
-h表示host,-h-P均为默认本机
两种架构
B/S:Browser Server
C/S:Client Server
1 JDBC(Java Database Connectivity)
是一个独立于特定数据库管理系统、通用的SQL数据库存储和操作的公共接口,接口封装了一系列抽象方法,定义了用来访问数据库的标准Java类库(java.sql.javax.sql),使用这些类库可以以一种比较标准的方法,方便地访问数据库资源。
2 JDBC程序编写步骤
2.1 获取数据库连接方式一:
2.1.1 Driver接口实现类
java.sql.Driver接口是所有JDBC驱动程序需要实现的接口。由数据库厂商提供,不同数据库厂商提供不同的实现。
Driver driver = new com.mysql.jdbc.Driver();
2.1.2 URL
String url = "jdbc:mysql://localhost:3306/mydb";
jdbc:协议
mysql:子协议
localhost:ip
3306:端口号
mydb:数据库名称
?:可添加关键字
2.1.3 info
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "abc123");
用一个Properities封装数据库的用户名和密码
2.1.4 获得连接
Connection connection = driver.connect(url, info);
合起来:
@Test
public void testConnection() throws SQLException {
Driver driver = new com.mysql.jdbc.Driver();
String url = "jdbc:mysql://localhost:3306/mydb";
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "abc123");
Connection connection = driver.connect(url, info);
System.out.println(connection);
}
2.2 方式二 :利用反射对方式一进行迭代
@Test
public void testConnection1() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
String url = "jdbc:mysql://localhost:3306/mydb";
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "abc123");
Connection connection = driver.connect(url, properties);
}
利用反射的动态性动态获取数据库驱动Driver实现类
2.3 方式三:使用DriverManager替换Driver
@Test
public void testConnection2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
DriverManager.registerDriver(driver);
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "abc123";
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
DriverManager.registerDriver(driver):注册驱动
2. 4 方式四:可以只是加载驱动,不用显示地注册驱动了
@Test
public void testConnection3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "abc123";
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
这一方面是由于反射机制会加载Driver类到内存,另一方面Driver父类com.mysql.cj.jdbc.Driver中存在静态代码块,在加载到内存时就进行了隐式地注册。
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
2.5 方式五(伪):java.sql.Driver文件在jar包导入时就加载了Driver父类com.mysql.cj.jdbc.Driver
@Test
public void testConnection4() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "abc123";
// Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
2.5 方式五(final版)配置文件
@Test
public void testConnection5() throws IOException, ClassNotFoundException, SQLException {
//1.读取配置文件的四个信息:通过类的系统类加载器读取配置文件
InputStream inputStream = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(inputStream);
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driverClass = properties.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获得连接
Connection connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
}
实现了数据和代码的分离,实现了解耦
如果需要修改配置文件信息,可以避免项目重新打包
3 操作和访问数据库
数据库连接用于向数据库发送命令和SQL语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。
在java.sql包中有3个接口分别定义了对数据库的调用的不同方式:
3.1 Statement:用于向数据库发送SQL语句
@Test
public void testLogin() throws IOException, ClassNotFoundException, SQLException {
InputStream resourceAsStream = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties info = new Properties();
info.load(resourceAsStream);
String url = info.getProperty("url");
String user = info.getProperty("user");
String pwd = info.getProperty("password");
String driverClass = info.getProperty("driverClass");
Class.forName(driverClass);
Connection connection = DriverManager.getConnection(url, user, pwd);
String sql = "SELECT * FROM USER WHERE uer = 'AA' AND pwd = '123456'";
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql);
while(rs.next()) {
System.out.println(rs.getString(1) + " " + rs.getString(2));
}
}
Statement的缺陷:
①需要拼写sql语句
②存在sql注入问题:利用某些系统没有对用户数据进行充分的检查,在用户输入的数据组注入非法的SQL语句段或命令,从而利用SQL引擎完成恶意行为的做法。
SELECT * FROM USER WHERE uer = '1' OR ' AND pwd = ' =1 OR '1' = '1'
③导致恶意攻击
3.2 PrepatedStatement:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句
@Test
public void preparedStatementTest() throws ClassNotFoundException, SQLException, IOException {
InputStream resourceAsStream = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties info = new Properties();
info.load(resourceAsStream);
String url = info.getProperty("url");
String user = info.getProperty("user");
String pwd = info.getProperty("password");
String driverClass = info.getProperty("driverClass");
Class.forName(driverClass);
Connection connection = DriverManager.getConnection(url, user, pwd);
String sql = "INSERT INTO USER(uer, pwd) VALUES (?, ?)";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, "111111");
ps.setString(2, "111111");
boolean result = ps.execute();
ps.close();
connection.close();
System.out.println(result);
}
execute方法返回一个boolean值,如果第一个结果是ResultSet对象则返回true,如果是更新计数或者没有返回结果则返回false。所以执行插入或者更新语句应该使用executeUpdate。
用于获取关闭连接的JDBCUtils工具类:
public class JDBCUtils {
public static Connection getConnection() {
Connection conn = null;
InputStream resourceAsStream = null;
try {
resourceAsStream = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties info = new Properties();
info.load(resourceAsStream);
String user = info.getProperty("user");
String password = info.getProperty("password");
String url = info.getProperty("url");
String driverClass = info.getProperty("driverClass");
Class.forName(driverClass);
conn = DriverManager.getConnection(url, user, password);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
}
return conn;
}
public static void resourceClose(Connection conn, Statement statement) {
try {
conn.close();
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
可用作实现增删改的通用方法:
public void update(String sql, Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++)
ps.setObject(i+1, args[i]);
ps.execute();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, ps);
}
}
测试:
@Test
public void testUpdate() throws ParseException {
String sql = "insert into user(uer,pwd,birthday) values(?,?,?)";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = sdf.parse("1997-05-30");
java.sql.Date birthday =new Date(date.getTime());
// System.out.println(birthday);
update(sql, "my", "pwdd", birthday);
}
注意这里通过sql.Date毫秒数的构造器实现了util.Date与sql.Date的转换:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = sdf.parse("1997-05-30");
java.sql.Date birthday =new Date(date.getTime());
ResultSet查询结果集
@Test
public void queryTest() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String sql = "select uer, pwd, birthday from user where id=?";
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, 14);
rs = ps.executeQuery();
while(rs.next()) {
String user = rs.getString(1);
String pwd = rs.getString(2);
System.out.println(user + ", " + pwd);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, ps, rs);
}
}
区别于迭代器的next,结果集的next方法判断为空的同时还进行了指针下移
★★★★★利用反射实现通用查询操作:(ResultSetMeta确定字段列数和列名)
public User query(String sql, Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
User user = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
Class clazz = User.class;
while(rs.next()) {
ResultSetMetaData metaData = rs.getMetaData();
user = new User();
for(int i=0;i<metaData.getColumnCount();i++) {
Object value = rs.getObject(i+1);
String value_name = metaData.getColumnName(i+1);
// String value_set_method = "set" + value_name.substring(0,1).toUpperCase(Locale.ROOT) + value_name.substring(1);
// System.out.println(value_set_method);
// Method method = clazz.getMethod(value_set_method, String.class);
// method.invoke(user, value);
Field field = clazz.getDeclaredField(value_name);
field.setAccessible(true);
field.set(user, value);
}
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn,ps,rs);
}
return user;
}
这里只能使用反射Field的set方法,使用Method的invoke方法不能确定参数类型(实际也能通过ResultSetMetaData获取数据库字段类型对应java数据类型,但是很麻烦)
@Test
public void queryTest1() {
String sql = "select uer,pwd,birthday from User where id = ?";
User user = query(sql, 14);
System.out.println(user);
}
针对数据库字段名和java类属性名不一致的情况:
①必须使用java类的属性名命名字段的别名
String sql = "select user_uer uer,user_pwd pwd,user_birthday birthday from User where id = ?";
②ResultSetMetaData的getColumnName()方法获取的是数据库的字段名,getColumnLabel()方法获取的是别名
★利用泛型方法实现对不同表的查询操作
public <T> T query(Class<T> clazz, String sql, Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
T t = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
while(rs.next()) {
ResultSetMetaData metaData = rs.getMetaData();
t = clazz.newInstance();
for(int i=0;i<metaData.getColumnCount();i++) {
Object value = rs.getObject(i+1);
String value_name = metaData.getColumnName(i+1);
// String value_set_method = "set" + value_name.substring(0,1).toUpperCase(Locale.ROOT) + value_name.substring(1);
// System.out.println(value_set_method);
// Method method = clazz.getMethod(value_set_method, String.class);
// method.invoke(user, value);
Field field = clazz.getDeclaredField(value_name);
field.setAccessible(true);
field.set(t, value);
}
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn,ps,rs);
}
return t;
}
PreparedStatement为什么能防止SQL注入?
PreparedStatement实行预编译,在还没有填充占位符的时候就确定了sql语句的逻辑结构。
PreparedStatement可以使用流填充,因此可以操作Blob型数据向数据库传输视频、图片等。
PreparedStatement可以实现更高效的批量操作:对于sql语句只需要校验一次然后缓存到PreparedStatement对象中,之后只需要对占位符进行填充即可。
Blob数据
@Test
public void BlobTest() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select photo from user where id=23";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()) {
Blob blob= rs.getBlob(1);
InputStream is = blob.getBinaryStream();
FileOutputStream fos = new FileOutputStream("2B.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
} catch (SQLException | FileNotFoundException throwables) {
throwables.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
类型 | 大小(字节) |
---|---|
TinyBlob | 255 |
Blob | 65K |
MediumBlob | 16M |
LongBlob | 4G |
批量插入数据
①update、delete本身就具有批量操作的能力。此处的批量操作,主要是指批量插入:
@Test
public void testInsert1() {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
String sql = "insert into goods(name) values(?)";
// boolean flag = true;
for(int i=0;i<20000;i++) {
ps = conn.prepareStatement(sql);
String str = "goods" + (i + 1);
ps.setString(1, str);
ps.executeUpdate();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if(conn != null) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
try {
if(ps != null) {
ps.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
②此种方式每次填充完占位符都需要与数据库交互,效率较低,可以存积一定数量ps数据库操作再与数据库交互:
for(int i=0;i<20000;i++) {
String str = "goods" + (i + 1);
ps.setString(1, str);
ps.addBatch();
if((i+1)%500==0) {
ps.executeBatch();
ps.clearBatch();
}
}
mysql默认服务器是关闭批处理的,我们需要一个在配置文件中加一个url参数:
url=jdbc:mysql://localhost:3306/mydb?rewriteBatchedStatements=true
时间由20s变为277ms
③conn.setAutoCommit(false):设置连接不可自动提交数据
conn = JDBCUtils.getConnection();
conn.setAutoCommit(false);
String sql = "insert into goods(name) values(?)";
ps = conn.prepareStatement(sql);
// boolean flag = true;
long stime = System.currentTimeMillis();
for(int i=0;i<1000000;i++) {
String str = "goods" + (i + 1);
ps.setString(1, str);
ps.addBatch();
if((i+1)%500==0) {
ps.executeBatch();
ps.clearBatch();
}
}
conn.commit();
long etime = System.currentTimeMillis();
System.out.println(etime - stime);
3.3 数据库事务
事务:一组逻辑单元,使数据库从一种状态变换到另一种状态。
事务处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种工作方式。当一个事务中执行多个操作,要么所有的事务都被提交(commit),那么这些修改就被永久地保存下来,要么数据库放弃所有修改,整个事务回滚(rollback) 到最初状态。
哪些操作会导致数据库的自动提交?
①DDL操作一旦执行,都会自动提交,set autocommit = false对DDL操作无效。
②DML默认情况会自动提交,可以通过set autocommit = false的方式取消DML操作的自动提交。
③
DML(Data Manipulation Language)数据操纵语言:
适用范围:对数据库中的数据进行一些简单操作,如insert,delete,update,select等.
DDL(Data Definition Language)数据定义语言:
适用范围:对数据库中的某些对象(例如,database,table)进行管理,如Create,Alter和Drop.
@Test
public void updateTest() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
conn.setAutoCommit(false);
// System.out.println(conn.getAutoCommit());
String sql1 = "update user set balance = balance - 100 where uer=?";
update(conn, sql1, "AA");
System.out.println(1/0);
String sql2 = "update user set balance = balance + 100 where uer=?";
update(conn, sql2, "2B");
System.out.println("转账成功");
conn.commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
try {
if(conn!=null) {
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
##### ※ 代码块在子类对象生成前获取父类的泛型参数,
private Class clazz = null;
{
//代码块在子类对象生成前获取父类的泛型参数
Type genericSuperclass = this.getClass().getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type[] typeArguments = parameterizedType.getActualTypeArguments();
clazz = (Class<T>) typeArguments[0];
}
这么做的目的是为了不用因为反射需要,每次都写明类型
public List getForList(Connection conn, String sql, Object...args) {
List list = new ArrayList<>();
PreparedStatement ps = null;
T t = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for(int i=0;i<args.length;i++) {
ps.setObject(i+1, args[i]);
}
rs = ps.executeQuery();
while(rs.next()) {
ResultSetMetaData metaData = rs.getMetaData();
t = clazz.newInstance();
for(int i=0;i<metaData.getColumnCount();i++) {
Object value = rs.getObject(i+1);
Field field = clazz.getDeclaredField(metaData.getColumnName(i+1));
field.setAccessible(true);
field.set(t, value);
}
list.add(t);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} finally {
JDBCUtils.resourceClose(null, ps, rs);
}
return list;
}
4 数据库连接池
在使开发基于数据库的web程序时,基本按照以下步骤:先在主程序中建立数据库连接,然后执行sql操作,最后断开sql连接。
这种开发模式存在的问题:
普通的JDBC数据库连接使用DriverManager获取,每次连接数据库都需要将Connection加载到内存,并且每次都需要验证用户名密码花费时间。数据库连接资源没有得到很好的重复利用。
每次连接使用完后都得断开,如果程序出现异常没有关闭,将会导致数据库系统中的内存泄漏,最终导致重启数据库。
内存对象不能被JVM回收的情况,称为内存泄漏
这种开发不能控制被创建的连接对象数,系统资源毫无顾忌地分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
4.1 多种开源的数据库连接池
JDBC的数据库连接池java.sql.DataSource来表示,DataSource只是一个接口,该接口由服务器(Weblogic,WebSphere,Tomcat)提供实现。
C3P0
方式一:硬编码方式创建数据库连接池
@Test
public void test() throws Exception {
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "com.mysql.cj.jdbc.Driver" ); //loads the jdbc driver
cpds.setJdbcUrl( "jdbc:mysql://localhost/mydb" );
cpds.setUser("root");
cpds.setPassword("abc123");
//设置初始时数据库连接池的参数
cpds.setInitialPoolSize(10);
Connection connection = cpds.getConnection();
System.out.println(connection);
}
方式二:通过配置文件设置数据库连接池
<c3p0-config>
<named-config name="HelloC3P0">
<!-- 提供获取连接的4个基本信息 -->
<property name="DriverClass">com.mysql.cj.jdbc.Driver</property>
<property name="JdbcUrl">jdbc:mysql://localhost:3306/mydb</property>
<property name="User">root</property>
<property name="Password">abc123</property>
<!-- 数据库连接池管理的基本信息 -->
<!-- 当数据库连接池中的连接数不足时,数据库连接池一次性申请的连接数-->
<property name="acquireIncrement">5</property>
<!-- C3P0数据库连接池维护的初始化连接数 -->
<property name="initialPoolSize">10</property>
<!-- C3P0数据库连接池维护的最少连接数 -->
<property name="minPoolSize">10</property>
<!-- C3P0数据库连接池维护的最多连接数 -->
<property name="maxPoolSize">100</property>
<!-- C3P0数据库连接池维护的最多Statement的个数-->
<property name="maxStatements">0</property>
<!-- 每个连接最多使用的Statement个数-->
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
获取连接
@Test
public void test1() throws SQLException {
ComboPooledDataSource cpds = new ComboPooledDataSource("HelloC3P0");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
DBCP
Druid(德鲁伊)数据库连接池
配置文件
url=jdbc:mysql://localhost:3306/mydb
username=root
password=abc123
driverClassName=com.mysql.cj.jdbc.Driver
initialSize=10
maxActive=10
@Test
public void test2() throws Exception {
Properties properties = new Properties();
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
properties.load(resourceAsStream);
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection conn = dataSource.getConnection();
System.out.println(conn);
}
5 Apache DBUTILS
commons-dbutils是Apache组织提供的一个开源JDBC工具类库,封装了针对于数据库的增删改查操作。
DButils增删改操作
@Test
public void test() {
Connection conn = null;
try {
QueryRunner qr = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "insert into user(user,balance) values(?,?)";
int insert_count = qr.update(conn, sql, "1A", 100000);
System.out.println(insert_count);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}
DButils查询操作
runner参数包括:
query(Connection conn, String sql, ResultSetHandler rsh, Object... params)
其中ResultSetHandler为接口,需要创建一个接口实现类,可供使用的接口实现类有:
AbstractKeyedHandler, AbstractListHandler, ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, ColumnListHandler, KeyedHandler, MapHandler, MapListHandler, ScalarHandler
BeanListHandler 用于返回一条数据
BeanHandler<User> handler = new BeanHandler<>(User.class)
@Test
public void test1() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select user,balance from user where id = ?";
BeanHandler<User> handler = new BeanHandler<>(User.class);
User user = runner.query(conn, sql, handler, 1);
System.out.println(user);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}
BeanListHandler用于返回多条数据
BeanListHandler<User> handler = new BeanListHandler<>(User.class);
@Test
public void test2() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select user,balance from user";
BeanListHandler<User> handler = new BeanListHandler<>(User.class);
List<User> list = runner.query(conn, sql, handler);
list.forEach(System.out::println);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}
MapListHandler将字段作为key,字段值为value返回map组成的list
@Test
public void test3() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select user, balance from user";
MapListHandler handler = new MapListHandler();
List<Map<String, Object>> list = runner.query(conn, sql, handler);
list.forEach(System.out::println);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}
ScalarHandler用于特殊查询
@Test
public void test4() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select count(*) from user";
ScalarHandler handler = new ScalarHandler();
Long count = (Long) runner.query(conn, sql, handler);
System.out.println(count);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.resourceClose(conn, null);
}
}
这里只能用Long
【Java Se】JDBC的更多相关文章
- 【Java编程】JDBC注入攻击-Statement 与 PreparedStatement
在上一篇[Java编程]建立一个简单的JDBC连接-Drivers, Connection, Statement and PreparedStatement我们介绍了怎样使用JDBC驱动建立一个简单的 ...
- 【Java SE】使用Javacv截取视频图片
[Java Web开发学习]使用Javacv截取视频图片 转载:https://www.cnblogs.com/yangchongxing/p/9482935.html github地址:https: ...
- 【Java SE】如何用Java实现冒泡排序
摘要: 作为一名Java开发工程师,手头如果不会几个常见的排序算法,怎么可能经过笔试题这一关呢.据我所知,许多大型的公司的笔试题都有排序题,那我们先从最简单的排序:冒泡排序开始,以后几篇博客将继续更新 ...
- 【Java SE】如何用Java实现直接选择排序
摘要:直接选择排序属于选择排序的一种,但是它的排序算法比冒泡排序的速度要快一些,由于它的算法比较简单,所以也比较适合初学者学习掌握. 适宜人群:有一定Java SE基础,明白Java的数据类型,数组的 ...
- 【Java SE】如何用Java实现插入排序
摘要:前面三期分别写了三篇简单排序的算法,今天来讲一点稍微难一点的排序算法-----插入排序. 基本思想: 设n个数据已经按照顺序排列好(假定从小排到大). 输入一个数据x,将其放在恰当的位置,使其顺 ...
- 【Java SE】如何用Java实现反转排序
摘要:反转排序是将原先已经排序好了的重新排序,是原来的数组元素的顺序反转过来.假设原来的数组顺序是{6,5,4,3,2,1},反转之后的顺序就是{1,2,3,4,5,6}.这个排序的算法不是很难,代码 ...
- 【Java SE】利用Java的for循环加random制作小学试卷
前期介绍:很多同学以为学习一门编程语言,一定要学到很高深的时候才可以做项目,其实不然,很多时候我们不需要学到面向对象的思想,就可以从事一些小项目的开发,来增加自己对开发的热情,比如现在我就可以利用Ja ...
- 【Java基础】JDBC简明教程
目录 1. 常用类 2. JDBC编程步骤 3. 事务处理 4. 数据库连接池 5. JDBC列子代码 6. 使用Apache的JDBC工具类 虽然在平时的开发过程中我们不会直接使JDBC的API来操 ...
- 【Java SE】如何安装JDK以及配置Java运行环境
摘要:不管是作为苦逼的Java码农,还是高端大气的Java系统架构师,如果不会安装JDK以及配置Java运行环境,那就巧妇难为无米之炊,不能进行Java后续的代码编写.当然如果你是Myeclipse编 ...
- 【java se】java注解
目录: 1.注解概述 2.常见的 Annotation 示例 3.自定义 Annotation 4.JDK 中的元注解 5.利用 反射获取注解信息(在反射部分 涉及) 6.JDK 8 中注解的新特性 ...
随机推荐
- JAVA - 判断两个浮点数相等
JAVA - 判断两个浮点数相等 背景知识 float型和double型是JAVA的基本类型,用于浮点数表示,在JAVA中float型占4个字节32位,double型占8个字节64位,一般比较适合用于 ...
- numpy基本使用(一)
一.简介 NumPy(Numerical Python) 是用于科学计算及数据处理的Python扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库. 二.数据结构 n ...
- JMeter常用组件(全)
JMeter执行顺序: 配置元件 → 前置处理器 → 定时器 → 取样器 → 后置处理器 → 断言 → 监听器: 同一层级的,顺序执行: 一.线程组 场景设置,模拟并发用户发送请求,设置并发策略.详见 ...
- C++学习流程
一.初级入门阶段 数据类型.变量.内存布局.指针基础: 字符串.一维数组.二维数组: 一级指针,二级指针,三级指针,N级指针概念,指针数组和数组指针: 结构体.文件的使用: 动态库的封装和设计: 函数 ...
- leedcode 刷题-V2
leetcode 字符串类 数组类 链表类 树类 二叉树类 图类 数学类 数据结构类 1. 稀疏相似度 (倒排索引) (https://leetcode-cn.com/problems/sparse- ...
- scaled logy
library(ggplot2) set.seed(1) vals1 <- rbeta(1000, 0.5, 0.1) vals2 <- rbeta(1000, 0.25, 0.3) gg ...
- python读书笔记-网页制作
socket()函数 Python 中,我们用 socket()函数来创建套接字,语法格式如下: Socket 对象(内建)方法 Python Internet 模块:
- 输入一段字符(只含有空格和字母,保证开头不为空格),里面有若干个字符串,求这些字符串的长度和,并输出最长字符串内容,如果有多个输出最先出现的那个字符串。以stop作为最后输入的字符串。
#include<stdio.h>#include<string.h>main(){ int i,j=0,m,count,max; char a[100]; ...
- Windows10远程桌面连接CentOS7图形化桌面
Step1:在Centos7上检查是否安装了epel库 执行命令:rpm -qa|grep epel 示例: [root@master ~]# rpm -qa|grep epel[root@maste ...
- 西电oj135题 拼数字并排序
类别综合 时间限制 1S 内存限制 1000Kb 问题描述 对于输入的字符串(只包含字母和数字),将其中的连续数字拼接成整数,然后将这些整数按从大到小顺序输出.例如字符串"abc123d5e ...