PreparedStatement
1. PreparedStatement是Statement的子类,具有预编译的功能。可以将SQL语句变异之后在发送到数据库,具有余下优点
a) 由于编译之后才发送,解决了SQL注入问题,提高程序安全性
b)由于编译之后才发送,减轻了服务器压力,提高了程序性能
c) 由于可以设置占位符,是代码结构更清晰,提高代码可读性
2. 使用方式
Connection类中PreparedStatement prepareStatement(String sql)方法预编译SQL语句,获得一个PreparedStatement
SQL语句可以使用问号预设占位符
PreparedStatement类中多个setXxx()方法可以设置占位符的值,注意占位符从1开始
PreparedStatement类中int executeUpdate()可以预设SQL语句进行更新操作,返回影响行数,注意不要加参数
PreparedStatement类中ResultSet executeQuery()可以运行预设SQL语句进行查询操作,返回结果集
3. 查询代码显示
PreparedStatement st = conn.preparedStatement(“select id,name,password,email,birthday from user where name=?);
st.setString(1, name);
ResultSet rs = st.executeQuery();
4.更新代码示例
PreparedStatement st = conn.prepareStatement("insert into user(name,password,email,birthday) values(?,?,?,?)");
st.setString(1, user.getName());
st.setString(2, user.getPassword());
st.setString(3, user.getEmail());
st.setDate(4, new Date(user.getBirthday().getTime()));
st.executeUpdate();
大文本和二进制操作
1.1.1. 存储TEXT
PreparedStatement ps = conn.prepareStatement("insert into clob(file) values(?)");
File file = new File("src/cn/itcast/jdbc1/clob_blob/ClobDemo.java");
Reader reader = new FileReader(file);
ps.setCharacterStream(1, reader, (int) file.length());
ps.executeUpdate()
1.1.2. 读取TEXT
PreparedStatement ps = conn.prepareStatement("select file from clob");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Reader reader = rs.getCharacterStream(int columnIndex);
// 这个Reader就是从数据库中读取数据的流, 操作这个流来读取数据
}
1.2. BLOB
BLOB用来存储大段的二进制数据, 例如图片, 音频, 视频. LONGBLOB最大4G
1.2.1. 存储BLOB
PreparedStatement ps = conn.prepareStatement("insert into big_binary(file) values(?)");
File file = new File("src/cn/itcast/jdbc1/clob_blob/IMG_0007.jpg");
InputStream in = new FileInputStream(file);
ps.setBinaryStream(1, in, (int) file.length());
ps.executeUpdate();
1.2.2. 读取BLOB
PreparedStatement ps = conn.prepareStatement("select file from big_binary");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
InputStream in = rs.getBinaryStream(1);
// 这个InputStream就是从数据库中读取数据的流, 操作这个流来读取数据
}
2. 接口DAO
在实际应用中, 我们的JDBC代码通常都是在数据访问层, 也就是DAO层
为了解除Service层和DAO之间的耦合, 通常会定义一个DAO接口, 其中包含各种对实体的操作
由于实际操作数据库可能使用各种不同技术, 但只要都实现之前定义好的接口, Service层通过接口就可以调用DAO层的代码了
在更换底层DAO的时候, Service层的代码由于都是针对接口调用而不需要改变.
3. 工厂模式
场景和问题:java程序开发讲究面向借口,隐藏具体的实现类。
解决方案:可以使用工厂方法,又成为简单工厂的设计模式。
工厂模式中主要用createXXX()或getFactory()方法来返回接口,并且在该方法内部创建实现类。
工厂模式中主要涉及:接口,实现类,工厂类(返回接口并且创建实现类)和调用类。
工厂模式的本质:重在选择,在工厂模式中起重要作用的是各种类,工厂只提供功能接口;选择功能的实现:外部传入参数或者从配置文件中获取。
工厂模式使用的步骤:首先创建一个接口,然后创建若干个实现接口的类,第三创建工厂类并且提供返回接口的方法createXXX();最后在其他的类中调用工厂中的接口。
工厂模式是指在Service层中不直接创建DAO层对象, 而是通过一个工厂类来读取配置文件创建.
在更换DAO层的实现时只要修改配置文件就可以, 不需对所有Service中的代码更改, 提高代码的维护性.
写一个单例工厂, 工厂中根据配置文件生成DAO, 提供方法获取DAO.
4. 分页
通过ResultSet的滚动可以设置获取记录的位置, 但这样是从数据库中查询出所有数据, 然后再从结果中筛选结果, 性能非常低.
MySQL提供了分页语法. 在查询语句后可使用LIMIT关键字完成分页功能, 例如:
select * from user limit 40,20
查询从user表中取出从第41条开始的20条记录. 第一个参数表示忽略前面多少个, 第二个参数代表取多少个.
5. 可更新结果集、敏感结果集
conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet.TYPE_SCROLL_SENSITIVE
表示获得ResultSet之后是敏感的, 随数据库更新的. 但MySQL没有支持这项功能
ResultSet.CONCUR_UPDATABLE
表示获得ResultSet之后是可更新的, 例如:
rs.next();
rs.updateString("name", "updateName");
rs.updateRow();
可以将当前行的name属性改为updateName.