1.对数据库调用的不同方式
java.sql包下有3个接口
- Statement:用于执行静态SQL语句。
- PreparedStatement:SQL语句被预编译并存储在此对象中。
- CallableStatement:用于执行SQL存储过程。
2.使用Statement操作数据库
获取到connect对象后,创建statement对象,执行sql
Statememt st = connect.createStatememt();
ResultSet rs = st.executeQuery("select * from table");
(1)使用Statememt操作表的弊端
- 对于有参数的SQL语句需要拼串。
String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName
+ "' AND PASSWORD = '" + password
+ "'";
- 存在SQL注入问题
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:
SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1'
) ,从而利用系统的 SQL 引擎完成恶意行为的做法。
public class StatementTest {
@Test
public void login(){
Scanner scanner = new Scanner(System.in);
System.out.println("用户名:");
String userName = scanner.nextLine();
System.out.println("密 码:");
String password = scanner.nextLine();
String sql = "SELECT user,password FROM user_table WHERE user = '"+userName+"' AND password = '"+password+"'";
User user = getUser(sql, User.class);
if (user!=null){
System.out.println("登录成功!");
}else {
System.out.println("用户名或密码错误!");
}
}
public <T> T getUser(String sql,Class<T> clazz){
//放到外面是因为在finally中要用
T t = null;
Connection connect = null;
Statement st = null;
ResultSet rs = null;
try {
//1.加载配置文件
InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
//2.读取配置文件
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
//3.加载驱动
Class.forName(driverClass);
//4.获取连接
connect = DriverManager.getConnection(url, user, password);
//5.创建statement对象
st = connect.createStatement();
//6.执行sql,得到结果集
rs = st.executeQuery(sql);
//7.获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//有多少列
int columnCount = rsmd.getColumnCount();
if (rs.next()){
t =clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
//1.获取列别名
String columnName = rsmd.getColumnLabel(i+1);
//2.根据列名获取数据
Object columnVal = rs.getObject(columnName);
//3.将得到的数据封装进对象
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t,columnVal);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
}
3.ResultSet和ResultSetMetaData
(1)ResultSet
执行executeQuery()方法返回结果集,是一个ResultSet对象,相当于一张数据表,有一个指针指向第一行记录。使用next()方法检测下一行是否有效,若有效返回true并移动到下一行。相当于Iterator对象的hasNext()和next()方法的结合体。
当指向一行时,通过索引或列名获取该列的值。getInt(1),getString("name")
Java与数据库交互涉及到的相关Java Api中的索引都从1开始。
(2)ResultSetMetaData
用于获取ResultSet对象中列的类型和属性信息的对象
ResultSetMetaData rsmd = resultSet.getMetaData();
常用方法
getColumnName(int column); //获取指定列名称
getColumnLabel(int column); //获取指定列别名,没有别名则获取列名
getColumnCount(); //有多少列
getColumnTypeName(int column); //列的数据库类型名称
getColumnDisplaySize(int column); //列的最大标准宽度,以字符为单位
isNullable(int column); //列中的值是否可以为null
isAutoIncrement(int column); //是否为指定列进行自动编号
例如表结构
SQL语句为,如果balance不选,则columnCount为2
SELECT user,password,balance FROM user_table WHERE user = 'AA' AND password = '123456'
两者之间的联系
4.PrepareStatement操作数据库
PrepareStatement是Statement的子接口。
public interface PreparedStatement extends Statement
获取对象,表示一条预编译过的SQL语句
PrepareStatement ps = connect.prepareStatement(sql);
SQL语句中的参数用英文问号表示,调用prepareStatement对象的setXxx(1,value)方法设置这些参数,1表示索引(从1开始),value表示值。
@Test
public void login(){
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String user = scanner.nextLine();
System.out.print("请输入密码:");
String password = scanner.nextLine();
// String sql = "SELECT user,password FROM user_table WHERE user = ? AND password = ? ";
String sql = "SELECT user,password FROM user_table WHERE user = ? and password = ?;";
User returnUser = getUser(User.class, sql, user, password);
if (returnUser!=null){
System.out.println("登录成功");
}else {
System.out.println("用户名或密码错误");
}
}
public <T> T getUser(Class<T> clazz,String sql,Object... args){
Connection connect = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connect = JDBCUtil.getConnection();
ps = connect.prepareStatement(sql);
//遍历参数并设置值
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
//ps.execute();//增删改执行这个
rs = ps.executeQuery(); //产生单个结果集
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
if (rs.next()){
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
Object columnValue = rs.getObject(i + 1); //列值
String columnLabel = rsmd.getColumnLabel(i + 1); //列别名
//通过反射获取对象属性
Field field = clazz.getDeclaredField(columnLabel);
//允许客户端拥有超级权限,不会去检查Java语言权限控制(private之类的)
field.setAccessible(true);
field.set(t,columnValue); //给对象t的属性设值
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(connect,ps,rs);
}
return null;
}
5.其他
(1)ORM思想
- 一张数据库表对应一个Java类
- 表中的一条记录对应Java类的一个对象
- 表中的一个字段对应Java类的一个属性
写sql时注意别名
(2)Java与SQL对应数据类型
Java类型 | SQL类型 |
---|---|
boolean | BIT |
byte | TINYINT |
short | SMALLINT |
int | INTEGER |
long | BIGINT |
String | CHAR,VARCHAR,LONGVARCHAR |
byte array | BINARY,VAR BINARY |
java.sql.Date | DATE |
java.sql.Time | TIME |
java.sql.Timestamp | TIMESTAMP |
(3)PrepareStatement的好处
① 可以防止SQL注入
② 预编译SQL语句可以重复使用,不同参数也不用重新编译(Statement每次执行都需要)
③ 不需要拼串
标签:JDBC,String,rs,数据库,SQL,PreparedStatement,user,sql,password From: https://www.cnblogs.com/zhishu/p/16874755.html参考
1.field.setAccessible(true) 简介
2.PreparedStatement是如何防止SQL注入的?