1.Class.forName(),是根据类的名字将类装载到虚拟机里面了。把类装载到虚拟机里面和创建一个类的实例是不一样的,创建一个实例就会有一个
实例产生,但是把类装载到虚拟机里面就不一定会有实例产生。
2.通过DriverManager.registerDriver()和System.setProperty()方式,会直接将驱动放入驱动列表里面。
3.通过Class.forName()方式,是将类加载到虚拟机里面,存在在虚拟机中的类的静态代码块会立即被虚拟机执行,所有的数据库开发商开发的驱动
类Driver里面都有一段相同的代码(因为要遵循sun的标准),代码块如下:
public class Driver extends NonRegisterDriver implements java.sql.Driver{
static {
try{
java.sql.DriverManager.registerDriver(new Driver());
}catch(SQLException e){
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException{
}
}
4.注册驱动是使用Class.forName()更好点,因为这样也可以注册一个驱动,而使用DriverManager.registerDriver()时,我们的参数是
new 了一个驱动对象,但是数据库厂商提供的驱动类里面的静态代码块依然会执行,这样就相当于产生了两个驱动类实例,虽然对
程序没有影响,但是Class.forName()形式的更好点。
5.如果我们的程序中没有导入数据库驱动jar包,那么DriverManager.registerDriver()形式就不能通过编译,因为我们 new 了一个对象
作为参数,所以这样就依赖于数据库驱动 jar 包,即:无法导入Driver类,而且更换数据库时也要修改程序代码,而Class.forName()
和System.setProperty()形式则可以编译,因为他们的参数都是字符串,没有依赖外部的数据库类,只有当程序运行时,实例化类时,
才报异常。更换数据库时不需要更改代码,只要更改属性文件即可。
6.JDBC url是跟着数据库变动的,格式为: JDBC:子协议:子名称//主机名:端口号/数据库名?属性名=属性值&...
mysql数据库的url: jdbc:mysql://localhost:3306/mydb 此url没有子名称,oracle就有,jdbc是数据库url的协议,类似万维网上的
http或者文件上传中的ftp等。如果我们使用的主机名和端口号都是默认的,则可以不写,例如上面的url也可以写成jdbc:mysql:///mydb
7.数据库连接(Connection)是非常稀有的资源用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用
原则是尽量晚创建,尽量早的释放。
8.当我们取得从数据库中获得的值时有两种方式的参数,一种是所以序列号,一种是字段列名,如果按照索引序列号则必须按照数据库中列的
顺序获得值,如果是按照字段列名则与顺序无关,可以任意顺序取。
9.sql注入时,我们只要输入一个 ' or 1 or' 即可。例如:
String name = "' or 1 or '"; //这两个单引号是要与sql语句中的前后两个单引号进行匹配的
sql = "select * from t_user where name ='" + name + "'";
组成的sql语句为:
select * from t_user where name =''or 1 or''
10.使用PreparedStatement和Statement的区别:
PreparedStatement会预处理sql语句,同时也可以屏蔽掉一下特殊字符,防止sql注入。其次,如果同一个sql执行次数比较多时PreparedStatement
比Statement的效率高,如果同一个Sql执行的次数比较少则效率Statement比PreparedStatement高。
11.PreparedStatement(从Statement扩展而来)相对Statement的优点:
1.没有SQL注入问题。
2.Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。
3.数据库和驱动可以对PreparedStatement进行优化(只有在相关联的数据库连接没有关闭的情况下有效)
12.处理大文本(即:把一个文件【中的内容】存入数据库):
String sql = "insert into clob_test(big_test) values(?)";
ps = conn.prepareStatement(sql);
File file = new File("src/cn/itcast/DBUtil3.java");
Reader reader = new BufferedReader(new FileReader(file));
ps.setCharacterStream(1,reader,(int)file.length());
//ps.setString(1,x); //可以将文件内容构建成一个String后存到数据库中,String没有大小限制,但是数据库中要定义成clob
int i = ps.executeUpdate();
13.获取数据库中的大文本类型:
Clob clob = ResultSet.getClob(1);
Reader reader = clob.getCharacterStream();
//reader = ResultSet.getCharacterStream(1);
//String s = rs.getString(1);
File file = new File("DBUtil3_bak.java");
Writer writer = new BufferedWriter(new FileWriter(file));
char[] buff = new char[1024];
for(int i = 0;(i = reader.read(buff)) > 0;){
writer.writer(buff,0,i);
}
14.处理二进制文件(例如:图片)
String sql = "insert into blog_test(big_bit) values(?)";
ps = conn.prepareStatement(sql);
File file = new File("IMG_001.jpg");
InputStream in = new BufferedInputStream(new FileInputStream(file));
ps.setBinaryStream(1,in,(int)file.length());
ps.executeUpdate();
15.读取Blob数据
//Blob blob = rs.getBlob(1);
//InputStream in = blob.getBinaryStream();
InputStream in = rs.getBinaryStream(1);
File file = new File("IMG_002.jpg");
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
byte[] buff = new byte[1024];
for(int i = 0;(i = in.read(buff)) > 0;){
out.write(buff,0,i);
}
16.一旦Connection关闭(断开)了,那么ResultSet中的数据也随即消失。所以当我们访问数据库时得到的ResultSet需要及时
处理,不能继续往上传递,除非我们不关闭Connection。即:在使用ResultSet之前不能关闭Connection连接。所以
Connection一旦关闭(断开)Statement和ResultSet就无效了。
17.异常处理:
从JDBC连接代码的那个文件中将异常往上抛,跑到调JDBC代码的DAO层时将异常捕获,这时捕获到的异常为SQL异常,我们不能
再将这个SQL异常继续往上跑,那样会污染我们service层的代码,所以我们要在DAO层将这个SQL异常装换为运行时异常进行处
理,代码如下:
自定义异常类:
public class DaoException extends RuntimeException {
public DaoException() {
}
public DaoException(String message) {
super(message);
}
public DaoException(Throwable cause) {
super(cause);
}
public DaoException(String message, Throwable cause) {
super(message, cause);
}
}
捕捉异常的DAO层代码:
public User findUser(String id) {
try {
String sql = "";
conn = dbutil.getInstance().getConnection();
state = conn.createStatement();
rs = state.executeQuery(sql);
while(rs.next()){
u = new User();
}
} catch (SQLException e) {
throw new DaoException(e.getMessage(),e);
}finally{
dbutil.free(rs, state, conn)
}
return u;
}
18.动态切换DAO层代码
使用工厂方法模式替换Dao层实现类:
属性文件中:src/daoconfig.properties
userDaoClass=com.java_min.DaoImpl.UserDaoImpl
工厂类中:
public class DaoFactory{
private UserDao userDao = null;
private static DaoFactory instance = new DaoFactory();
private DaoFactory(){
Properties prop = new Properties();
InputStream in = new FileInputStream(new File("src/daoconfig.properties"));
prop.load(in);
String userDaoClass = prop.getProperty("userDaoClass");
userDao = (UserDao)Class.forName(userDaoClass).newInstance();
}
public static DaoFactory getInstence(){
return instance;
}
public UserDao getUserDao(){
return userDao;
}
}
总结:工厂一般都是用单例的,其次我们用实现DAO层的动态切换就用使用读取属性文件的方式,读取
实现类的全路径表示,后使用反射机制得到DAO层实现类的实例,此处读取属性文件的方式有
两种多种方式,上面是使用了文件流对象读取属性文件,我们还可以使用“类加载器”读
取属性文件,代码入:
Properties prop = new Properties();
InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("daoconfig.properties");
prop.load(in);
“类加载器”是每个类都有的,它可以加载“类.class”还可以加载其他东西,如上面的属性文件,文件流与类加载器加载
属性文件的不同之处在于,文件流加载时,需要给出属性文件的路径,当使用类加载器加载时我们不需要给出属性文件
的路径,只需要将属性文件放入classpath目录下即可,类加载器会从classpath目录下寻找的。
19.事物保存点处理:
有事事务回滚时,我们我们并不想让所有的事务都回滚,而是回滚到某个点上。可以使用事务保存点。代码如下:
Savepoint sp;
try{
String sql = "";
state.executeUpdate(sql);
sp = conn.setSavepoint(); 1.
sql = "";
state.executeUpdate(sql);
sql = "";
rs = state.executeQuery(sql);
while(rs.next()){
throw new RuntimeExcetion();
}
}catch(Exception){
if(conn != null && sp != null){
conn.rollback(sp); //如果不带参数就会全部回滚,带了参数就会回滚到此参数指定的事务保存点上
conn.commit();
}
}finally{
if(conn != null){
conn.rollback();
}
}
以上就会回滚到1.出,只有第一条语句执行成功提交,后面的那个更新被回滚。
20.数据库隔离级别(假如有两个人A、B同时修改数据库,两个人操作数据库的时候就是个子开启个子的事务,两个事务互不干扰,即:隔离性):
未提交读:假如A修改了数据库中的数据,但是还没有提交事务,这是B去读数据也能读到刚才A修改的数据,如果A此时回滚了事务,那么B读取的这个数据就是脏数据了。
提交读:假如A修改了数据库中的数据,但是还没有提交事务,这是B去读数据是读不到A刚才修改的数据的,如果A此时提交了事务,那么B就可以读取这个数据了。
可重复读:假如A读取了数据库总的数据,此时B在A读取了数据后修改了数据库中的数据,也提交了事务,那么A读取的数据是不会改变的,如果A再重新读取数据也是读取不到的,
除非A从新开启一个事务就可以读取B修改后的数据了。
序列化:就是相当于数据库加锁,如果A开启事务操作数据时(包括读取数据),那么B随后也开启事务操作数据,如果B提交事务时是不能提交事务的,那么事务会停下来等待A的
事务提交,如果A不提交事务,B的操作就会一直处于等待状态,一旦A提交了事务,那么B的事务马上就会结束等待,执行事务提交。
21.JDBC调用存储过程:
存储过程:
DELIMITER $$
DROP PROCEDURE IF EXISTS 'jdbc'.'addUser' $$
CREATE PROCEDURE 'jdbc'.'addUser'(in pname varchar(45),in birthday date,in money float,out pid int)
BEGIN
insert into user(name,birthday,momey) values(pname,birthday,money);
select last_insert_in() into pid; //last_insert_in 这是数据库中的一个函数,用于查询当前线程插入的数据的索引号
END $$
DELIMITER ;
java调用存储过程:
String sql = "{call addUser(?,?,?,?)}";
cs = conn.prepareCall(sql);
cs.registerOutParameter(4,Types.INTEGER);
cs.setString(1,"simier");
cs.setDate(2,new java.sql.Date(System.currentTimeMillis()));
cs.setFloat(3,100f);
cs.executeUpdate();
int id = cs.getInt(4);
22.批处理:
PreparedStatement:
String sql = "insert into t_user values(?,?)";
ps = conn.prepareStatement(sql);
for(int i = 0;i < 1000;i ++){
ps.setString(1,"name" + i);
ps.setFloat(2,100f + i);
ps.addBatch();
}
int[] is = ps.executeBatch();
------------------------------------------
Statement:
for(int i = 0;i < 1000;i ++){
String sql = "insert into t_user values(i,"name" + i)";
state.addBatch(sql);
}
state.executeBatch();
23.可滚动结果集与分页技术:
String sql = "select * from t_user where id < 5";
while(rs.next()){
System.out.println(rs.getString(0) + "----" + rs.getString(1));
}
System.out.println("*************");
rs.absolute(4); //将指针定位到结果集的第四条记录上
if(rs.previous()){
System.out.println(rs.getString(0) + "----" + rs.getString(1));
}
输出结果:
1 name1
2 name2
3 name3
4 name4
5 name5
*********
3 name3
我们可以使用absolute()方法实现分页技术,比如在数据库中从第100条记录开始
取10条记录(此分页属于内存分页,效率比较低,这种方式可以在数据库不支持数
据库分页时使用这种方法)。
rs.absolute(100);
int i = 0;
while(rs.next() && i < 10){
System.out.println(rs.getString(0) + "----" + rs.getString(1));
}
24.结果集更新:
while(rs.next()){
rs.updateFloat("money",300f);
rs.updateRow();
}
25.反射机制:
1.构造具有相同参数签名的对象的通用类,代码如下:
static Object create(Class clazz)throws Exception{ //动态传入一个参数,使用该参数创建一个对象
Constructor con = clazz.getConstructor(String.class);
Object obj = con.newInstance("simier"); //构造具有一个初始化参数的对象
return obj;
}
2.使用反射机制调用对象的方法,代码如下:
static void invoke(Object obj){
Method[] ms = obj.getClass().getMethods(); //获得该类的所有方法,以及超类中的方法
Method[] m = obj.getClass().getDeclaredMethods(); //获得自定义的所有方法,超类和私有的方法无法获得
for(Method i:m){
System.out.println(i);
}
}
3.使用反射机制精确调用对象的方法,代码如下:
static void invoke(Object obj){
Method m = obj.getClass().getMethod(methodName,null); //methodName是想要调用的方法的名字,null表示该方法不需要传入参数
m.invoke(obj,null); //执行该方法,obj必须是实例化的对象,这个方法接受的参数都必须是对象
}
3.使用反射机制额调用对象的属性,代码如下:
static void field(Class clazz){
Field fs = clazz.getDeclaredFields(); //得到该类的所有自定义属性
fs = clazz.getField(); //得到该类的所有public类型属性
}
26.编写一个基本的连接池实现连接的复用,代码如下:
package com.java_min.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
//连接池类
public class MyDataSource {
//此段代码可以放到JDBCUtil类中,当创建连接池对象时,以参数的形式传进来
/*private static String url = "jdbc:mysql://localhost:3306/jdbc";
private static String userName = "root";
private static String password = "root";
private static String driver = "com.mysql.jdbc.Driver";*/
private String url;
private String userName;
private String password;
private static int initCount = 5;
private static int maxCount = 10;
private int currentCount = 0;
private LinkedList<Connection> connectionPool = new LinkedList<Connection>();
//为了灵活性,此段代码也放在JDBCUtil类中
/*static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
new ExceptionInInitializerError(e);
}
}*/
public MyDataSource(String userName,String password,String url){
this.userName = userName;
this.password = password;
this.url = url;
try {
for(int i = 0;i < initCount;i++){
this.connectionPool.addLast(this.createConnection());
this.currentCount ++;
}
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public Connection getConnection() throws SQLException{
synchronized(connectionPool){
if(this.connectionPool.size() > 0){
return this.connectionPool.removeFirst();
}
if(this.currentCount < maxCount){
this.currentCount ++;
return this.createConnection();
}
throw new SQLException("已没有连接");
}
}
public void free(Connection conn){
this.connectionPool.addLast(conn);
}
private Connection createConnection() throws SQLException{
return DriverManager.getConnection(url,userName,password);
}
//此段代码放在JDBCUtil类中
/*public static void free(ResultSet rs,Statement state,Connection conn){
try {
if(rs != null){
rs.close();
}
} catch (SQLException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally{
try {
if(state != null){
state.close();
}
} catch (SQLException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}finally{
try {
if(conn != null){
myDataSource.free(conn);
}
} catch (SQLException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
}*/
}
27.开源DBCP数据库连接池:
需要导入的jar文件:
commons-collections-3-1.jar
commons-dbcp-1.2.2.jar
commons-pool.jar
创建数据源:
Properties ps = new Properties();
InputStream is = DBUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
ps.load(is);
DataSource ds = BasicDataSourceFactory.createDataSource(ps);
28.将DAO中的修改方法提取到抽象父类中,具体代码如下:
抽象父类:
public abstract class AbstractDao{
public int update(String sql,Object[] args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
conn = DBUtils.getConnction();
ps = conn.prepareStatement(sql);
for(int i = 0;i < args.length;i ++){
ps.setObject(i + 1,args[i]);
}
return ps.executeUpdate();
}catch(Exception e){
e.printStack();
}finally{
DBUtils.free(rs,ps,conn);
}
}
}
继承抽象父类的子类:
public class UserDaoImpl extends AbstractDao{
public void update(User user){
String sql = "update t_user set name=?,birthday=?";
Object[] args = new Object[]{user.getName(),user.getBirthday()};
int i = super.update(sql,args);
}
}
29.使用模板方法设计模式处理DAO中的查询方法,代码如下:
模板类:
public abstract class AbstractDao{
public Object find(String sql,Object[] arsg){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
conn = DBUtils.getConnection()
ps = conn.prepareStatement(sql);
for(int i = 0;i < args.length;i ++){
ps.setObject(i + 1,args[i]);
}
rs = ps.executeQuery();
Object obj = null;
while(rs.next()){
obj = rowMapper(rs);
}
}catch(Exception e){
e.printStack();
}finally{
DBUtils.free(rs,ps,conn);
}
}
protected abstract Object rowMapper(ResultSet rs);
}
子类代码:
public class UserDaoImpl extends AbstractDao{
public User getUser(String loginName,String password){
String sql = "select id,name,money from t_user where name=?";
Object[] args = new Object[]{loginName};
Object user = super.find(sql,args);
return (User)user;
}
protected Object rowMapper(ResultSet rs){
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setMoney(rs.getFloat("money"));
return user;
}
}
30.使用策略模式对模板方法设计模式进行改进,代码如下:
接口类:
public interface RowMapper{
public Object mapRow(ResultSet rs)throws SQLException;
}
模板类:
public class DaoTemplete{
public Object find(String sql,Object[] arsg,RowMapper rowMapper){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
conn = DBUtils.getConnection()
ps = conn.prepareStatement(sql);
for(int i = 0;i < args.length;i ++){
ps.setObject(i + 1,args[i]);
}
rs = ps.executeQuery();
Object obj = null;
while(rs.next()){
obj = rowMapper.mapRow(rs);
}
}catch(Exception e){
e.printStack();
}finally{
DBUtils.free(rs,ps,conn);
}
}
}
子类:
public class UserDaoImpl{
DaoTemplete template = new DaoTemplete();
public User findUser(String loginName,String password){
String sql = "select id,name,money,birthday from t_user where name=?";
Object[] args = new Object[]{loginName};
Object user = this.template.find(sql,args,new RowMapper(){ 1.
public Object mapRow(ResultSet rs)throws SQLException{
return rs.getString("name");
}});
return ((User)user).getName();
}
}
上面1.处的匿名内部类也可以使用一个实现了RowMapper接口的实现类的实例代替。
31.Spring框架中提供了一个JdbcTemplate工具类,JdbcTemplate类对JDBC API进行了很好的封装这个类就像我们自己
对JDBC进行封装一样,只是代码更健壮和功能更强大而已,我们以后在实际项目中可以使用JdbcTemplate类来完全
替代直接使用JDBC API,这与直接使用JDBC API没太大的性能区别,使用JdbcTemplate类需要额外从Spring开发包
中导入spring.jar和commons-logging.jar包。
org.springframework.jdbc.core.JdbcTemplate对象介绍:
构造JdbcTemplate对象时要给其传一个数据源类型的参数,有两种方式,构造对象时传入一个参数,或者构造完后使
用对象的setter方法将数据源设置进去。代码如下:
1. JdbcTemplate jt = new JdbcTemplate(org.apache.commons.BasicDataSource);
2. JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
JdbcTemplate对象API介绍:
1. jt.queryForObject(sql,args,rowMapper);
sql:为我们传入的预sql语句
args:为一个Object类型的数组,为填充我们传入的预sql语句的占位符
rowMapper:为一个行映射器接口
示例代码如下:
String sql = "select id,name,money from t_user where name=?";
Object[] args = new Object[]{name};
Object user = jt.queryForObject(sql,args,new RowMapper(){
public Object mapRow(ResultSet rs,int rowNum)throws SQLException{ //匿名内部类,回调函数
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setMoney(rs.getFloat("money"));
return user;
}
});
return (User)user;
2. Object user = jt.queryForObject(sql,args,argTypes,new BeanPropertyRowMapper(Class)); //只能返回一个结果,如果有多个结果该方法就会报异常
sql:我们传入的预处理sql语句
args:为一个Object类型的数组,用来填充我们预处理sql的占位符的
argTypes:用来定义传入的参数的类型,此参数可选,如果不选spring会根据反射机制获得每个传入的参数的类型
BeanPropertyRowMapper:此类实现了RowMapper接口,可以用来代替上面那个回调方法。但是使用此对象的时候
有一些限制条件,构造BeanPropertyRowMapper对象时必须要传入要查询对象的类,
之后BeanPropertyRowMapper对象会使用反射机制将查询出来的结果赋值到对应的对象
属性上去,所以此处条件为,java类中的属性必须于数据库中的字段同名,或者命名单
词相同,彼此都符合java和数据可的规范,如果java类中的属性命名与数据库中的字段
名不相同,那么可以在sql语句中使用别名的方式使之与java类中的属性同名。
示例代码如下:
JdbcTemplate jt = new JdbcTemplate(org.apache.commons.BasicDataSource);
String sql = "select id,user_name,money,user_birthday_date as birthday from t_user where name=?";
Object[] args = new Object[]{name};
//int[] argTypes = new int[]{Types.INTEGER};
//Object user = jt.queryForObject(sql,args,argTypes,new BeanPropertyRowMapper(User.class)); //加不加输入参数的类型定义,效果一样,不加Spring也会通过反射机制获得参数类型的
Object user = jt.queryForObject(sql,args,new BeanPropertyRowMapper(User.class));
return (User)user;
java代码:
private String id;
private String userName;
private float money;
private Date birthday;
总结,java类里面的属性一定要大于或者等于sql语句中的属性的数量。
3. List users = jt.query(sql,args,new BeanPropertyRowMapper(User.class)); //可以返回多个结构
sql:我们传入的预处理sql语句
args:为一个Object类型的数组,用来填充我们预处理sql的占位符的
BeanPropertyRowMapper:此类实现了RowMapper接口,可以用来代替上面那个回调方法。但是使用此对象的时候
有一些限制条件,构造BeanPropertyRowMapper对象时必须要传入要查询对象的类,
之后BeanPropertyRowMapper对象会使用反射机制将查询出来的结果赋值到对应的对象
属性上去,所以此处条件为,java类中的属性必须于数据库中的字段同名,或者命名单
词相同,彼此都符合java和数据可的规范,如果java类中的属性命名与数据库中的字段
名不相同,那么可以在sql语句中使用别名的方式使之与java类中的属性同名。
示例代码如下:
JdbcTemplate jt = new JdbcTemplate(org.apache.commons.BasicDataSource);
String sql = "select id,user_name,money,user_birthday_date as birthday from t_user where id<?";
Object[] args = new Object[]{id};
List users = jt.query(sql,args,new BeanPropertyRowMapper(User.class));
return users;
java代码:
private String id;
private String userName;
private float money;
private Date birthday;
总结:JdbcTemplate对象的query方法可以将查询出的结构组装成我们所给的匹配对象,后放入到List对象中,返回给我们
4. String sql = "select count(*) from t_user";
int n = jt.queryForInt(sql);
5. String sql = "select name from t_user where id=" + id;
Object name = jt.queryForObject(sql,String.class); //String.class 参数代表要返回的结果的期望类型,不给也可以,给了后该方法就会对结果进行校验,不会就不会校验
return (String)name;
6. String sql = "select id as userId,name,money,birthday from user where id=" +id;
Map map = jt.queryForMap(sql);
map对象中的内容为:{userId=1,name=simier,money=99999900.0,birthday=1985-06-01}该对象中的key名就为
我们sql语句中的字段名,注意那个id的别名,value值为数据库中对应字段的值。
7. String sql = "select id as userId,name,money from t_user where id=" +id;
jt.queryForList(sql);
此方法将返回一个List对象,其将查询出来的数据先放入一个Map后将Map放入List中,即:List中为一个个Map对象,而一个Map中存放了一条记录
8. jt.execute(new ConnectionCallback(){
public Object doInConnection(Connection conn)throws SQLException,DataAccessException{
String sql = "";
PreparedStatement ps = conn.prepareStatement(sql,Statement,RETURN_GENERATED_KEYS);
ps.setString(1,user.getName());
...
ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if(rs.next()){
user.setId(rs.getInt(1));
}
return user;
}});
32.使用支持命名参数的JdbcTemplate对象NamedParameterJdbcTemplate,代码如下:
NamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate(org.apache.commons.BasicDataSource);
String sql = "select id,name,money,birthday from t_user where name=:names and money > :m and id < :id"; //命名参数名定义随意
Map params = new HashMap();
params.put("names",user.getName());
params.put("m",user.getMoney());
params.put("id",user.getId());
Object u = named.queryForObject(sql,params,new BeanPropertyRowMapper(User.class)); //仅适合查找一个对象,少于一个或多于一个都会抛异常,命名参数为一个Map对象
以上代码也可以换成一下形式,下面代码更具有对象性,代码如下:
NamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate(org.apache.commons.BasicDataSource);
String sql = "select id,name,money,birthday from t_user where name=:name and money > :money and id < :id"; //命名参数名定义必须与JavaBean中的属性同名
SqlParameterSource ps = new BeanPropertySqlParameterSource(user); //user参数是一个User对象,Spring会根据反射到JavaBean中寻找与SQL语句中命名参数同名的属性,获取其值赋给SQL占位符
Object u = named.queryForObject(sql,params,new BeanPropertyRowMapper(User.class)); //仅适合查找一个对象,少于一个或多于一个都会抛异常,命名参数为一个BeanPropertySqlParameterSource对象
---------------------
NamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate(org.apache.commons.BasicDataSource);
String sql = "insert into t_user(name,birthday,money) values(:name,:birthday,:money)"; //此处不能使用占位符?必须使用与JavaBean中属性同名的命名参数
SqlParameterSource ps = new BeanPropertySqlParameterSource(user); //此处参数user为User对象
KeyHolder keyHolder = new GeneratedKeyHolder();
named.update(sql,ps,keyHolder); //此方法更新(插入)结果后,会返回所更新(插入)的记录的主键值,并将其放入keyHolder对象中
int id = keyHolder.getKey().intValue(); //如果是双主键可以使用方法getKeys(),该方法会返回一个Map对象,Map的key的值是表中字段的名字,value是表中字段的值
return id;
33.使用SimpleJdbcTemplate和泛型技术简化代码(JDK1.5),代码如下:
public class SimpleJdbcTemplateTest{
static SimpleJdbcTemplate simple = new SimpleJdbcTemplate(org.apache.commons.BasicDataSource);
static User find(String name){
String sql = "select id,name,money,birthday from t_user where name=? and money=?";
User user = simple.queryForObject(sql,ParameterizedBeanPropertyRowMapper.newInstance(User.class),name,100f); //此方法返回的类型有newInstance的参数决定
return user;
}
//为了更大的灵活性,以上查询方法可用以下代码(泛型)替换
static <T>T find(String name,Class<T> clazz){
String sql = "select id,name,money,birthday from t_user where name=? and money=?";
Object obj = simple.queryForObject(sql,ParameterizedBeanPropertyRowMapper.newInstance(clazz),name,100f); //此方法返回的类型有newInstance的参数决定
return obj;
}
}