JDBC的ORM技术
什么是ORM?
1)ORM=Object Relationship Database Mapping
2)对象和关系数据库的映射
3)简单说,一个对象,对应数据库里的一条记录
为什么要使用ORM技术?
用JDBC的API编程访问数据库,代码量较大,特别是访问字段较多的表的时候,代码显得繁琐、累赘,容易出错,例如:
public void addAccount(final Account account) throws DAOException {
final Connection conn=getConnection();
PreparedStatement pstmt=con.prepareStatment("insert into account value(?,?,?,?,?,?,?,?,?)");
pstmt.setString(1,account.getUserName());
pstmt.setInt(2,account.getPassWord());
pstmt.setString(3,account.getSex());
pstmt.setString(4,account.getQq());
......
pstmt.execute();
conn.Close();
}
//可见,程序员需要耗费大量的时间、精力去编写具体的数据库访问的SQL语句,
//还要十分小心其中大量重复的源代码是否有疏漏,并不能集中精力于业务逻辑开发上面。
ORM则建立了Java对象与数据库对象之间的影射关系,程序员不需要编写复杂的SQL语句,直接操作Java对象即可,从而大大降低了代码量,也使程序员更加专注于业务逻辑的实现。
采用JDBC编程,在很多时候存在效率低下的问题,如:
pstmt =conn.prepareStatement("insert into user_info values(?,?)");
for (int i=0; i<1000; i++) {
pstmt.setInt(1,i);
pstmt.setString(2,"User"+i.toString());
pstmt.executeUpdate();
}
//以上程序将向后台数据库发送1000次SQL语句执行请求,运行效率较低.
如果采用ORM技术,ORM框架将根据具体数据库操作需要,会自动延迟向后台数据库发送SQL请求,如上面的程序,只会在循环完成后,一次向数据库发送操作请求,从而大大降低通讯量,提高运行效率;ORM也可以根据实际情况,将数据库访问操作合成,尽量减少不必要的数据库操作请求。
下面是ORM的一个例子,提供方法get(int id),返回一个Hero对象
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class TestJDBC {
public static Hero get_id(int id) {
Hero hero = new Hero();
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url="jdbc:mysql://127.0.0.1:3306/how2j?characterEncoding=UTF-8";
String user="root";
String password="root";
try(
Connection c = DriverManager.getConnection(url, user, password);
Statement s = c.createStatement();
) {
String sql = "select * from hero where id = "+id;
ResultSet rs = s.executeQuery(sql);
if(rs.next()) {
hero.id=rs.getInt("id");
hero.name=rs.getString("name");
hero.hp=rs.getFloat("hp");
hero.damage=rs.getInt("damage");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
return hero;
}
}
public static void main(String[] args) {
Hero h=get_id(22);
System.out.println(h.id+" "+h.name+" "+h.hp+" "+h.damage);
}
}
class Hero{
public int id;
public String name;
public float hp;
public int damage;
}
JDBC的DAO技术
什么是DAO?
DAO=DataAccess Object数据访问对象
实际上就是把数据库相关的操作都封装在这个类里面,其他地方看不到JDBC的代码
新建一个接口DAO,里面有几个空方法
package jdbc;
import java.util.List;
public interface DAO {
//增加
public void add(Hero hero);
//修改
public void update(Hero hero);
//删除
public void delete(int id);
//获取
public Hero get(int id);
//查询
public List<Hero> list();
//分页查询
public List<Hero> list(int start, int count);
}
然后在类HeroDAO中去实现这个方法
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import jdbc.Hero;
public class HeroDAO implements DAO{
public HeroDAO() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2j?characterEncoding=UTF-8", "root",
"root");
}
public int getTotal() {
int total = 0;
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "select count(*) from hero";
ResultSet rs = s.executeQuery(sql);
while (rs.next()) {
total = rs.getInt(1);
}
System.out.println("total:" + total);
} catch (SQLException e) {
e.printStackTrace();
}
return total;
}
public void add(Hero hero) {
String sql = "insert into hero values(null,?,?,?)";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.execute();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
int id = rs.getInt(1);
hero.id = id;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public void update(Hero hero) {
String sql = "update hero set name= ?, hp = ? , damage = ? where id = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, hero.name);
ps.setFloat(2, hero.hp);
ps.setInt(3, hero.damage);
ps.setInt(4, hero.id);
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void delete(int id) {
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "delete from hero where id = " + id;
s.execute(sql);
} catch (SQLException e) {
e.printStackTrace();
}
}
public Hero get(int id) {
Hero hero = null;
try (Connection c = getConnection(); Statement s = c.createStatement();) {
String sql = "select * from hero where id = " + id;
ResultSet rs = s.executeQuery(sql);
if (rs.next()) {
hero = new Hero();
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.name = name;
hero.hp = hp;
hero.damage = damage;
hero.id = id;
}
} catch (SQLException e) {
e.printStackTrace();
}
return hero;
}
public List<Hero> list() {
return list(0, Short.MAX_VALUE);
}
public List<Hero> list(int start, int count) {
List<Hero> heros = new ArrayList<Hero>();
String sql = "select * from hero order by id desc limit ?,? ";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setInt(1, start);
ps.setInt(2, count);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Hero hero = new Hero();
int id = rs.getInt(1);
String name = rs.getString(2);
float hp = rs.getFloat("hp");
int damage = rs.getInt(4);
hero.id = id;
hero.name = name;
hero.hp = hp;
hero.damage = damage;
heros.add(hero);
}
} catch (SQLException e) {
e.printStackTrace();
}
return heros;
}
}
数据库连接池
传统方式
1)当有多个线程,每个线程都需要连接数据库执行SQL语句的话,那么每个线程都会创建一个连接,并且在使用完毕后,关闭连接。
2)创建连接和关闭连接的过程也是比较消耗时间的,当多线程并发的时候,系统就会变得很卡顿。
3)同时,一个数据库同时支持的连接总数也是有限的,如果多线程并发量很大,那么数据库连接的总数就会被消耗光,后续线程发起的数据库连接就会失败。
使用连接池方式
1)与传统方式不同,在使用连接之前,就会在连接池内创建好一定数量的连接。
2)如果有任何线程需要使用连接,那么就从连接池里面借用,而不是自己重新创建.
3)使用完毕后,又把这个连接归还给连接池供下一次或者其他线程使用。
4)倘若发生多线程并发情况,连接池里的连接被借用光了,那么其他线程就会临时等待,直到有连接被归还回来,再继续使用。
5)整个过程,这些连接都不会被关闭,而是不断的被循环使用,从而节约了启动和关闭连接的时间。
使用DBCP和C3P0连接池
在使用之前先导入一些jar包,见下图,导入方式见JDBC笔记1,具体的包百度下载。
初始化dbcp连接池
package jdbc;
import org.apache.commons.dbcp2.BasicDataSource;
public class DBCP {
static String url="jdbc:mysql://127.0.0.1:3306/how2j?characterEncoding=UTF-8";
static String user="root";
static String password="root";
//初始化JDBC连接池,初始化必须最先执行,所以放在静态代码块中
static {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl(url);
ds.setUsername(user);
ds.setPassword(password);
ds.setInitialSize(5);//设置连接池初始化的时候有5个连接
ds.setMinIdle(3);//设置连接池最小的空闲连接,比如初始的5个连接用完了3个,空闲的还有两个,那么再创建一个保持在3个空闲连接,避免突发大量连接到来新创建连接降低性能
ds.setMaxTotal(20);//设置最大连接数,如果没有这个限制,当大量连接到来,无限制的创建连接,设备可能承受不了
}
}
从连接池获取连接
package jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbcp2.BasicDataSource;
public class DBCP {
static String url="jdbc:mysql://127.0.0.1:3306/how2j?characterEncoding=UTF-8";
static String user="root";
static String password="root";
static BasicDataSource ds = null;
static {
ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl(url);
ds.setUsername(user);
ds.setPassword(password);
ds.setInitialSize(5);
ds.setMinIdle(3);
ds.setMaxTotal(20);
}
//创建一个方法,从连接池获取连接
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
使用获取的连接&释放连接
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class TestJDBC {
public static void main(String[] args) throws Exception {
Connection con = DBCP.getConnection();
Statement s = con.createStatement();
String sql = "select * from hero where id = 13";
ResultSet rs = s.executeQuery(sql);
while(rs.next()) {
int id = rs.getInt(1);
String name = rs.getNString(2);
float hp = rs.getFloat(3);
int damage = rs.getInt(4);
System.out.println(id+" "+name+" "+hp+" "+damage);
}
s.close();
con.close();//从连接池获取的连接,使用完毕后直接close会归还到连接池
}
}
c3p0
使用方式和dbcp一样,只是内部有区别,功能更强大,也更常用