DAO层的写法总结:
本文中进行的总结,主要针对的是持久层,并不需要Web工程的环境,只需要Java工程即可。
DAO层持有实体层的对象,DAO层的增删改查写法会很多。下面总结一下三种写法:这里只给出查询的写法,其他写法类似。
1、最原始的JDBC写法
2、Apache的 Commons DbUtils
3、Hhibernate 或 Mybatis方式
其中,方式1大家应该都会了,方式2是黑马视频中提过的,我们主要是理解。方式3里的Mybatis在本文中有详细的代码和结构、配置展示。
mysql 5.5 建库、建表、两行示例数据的语句:(账号和密码都是root)
create database studentdb;
use studentdb;
create table student(
id int primary key auto_increment,
name varchar(20),
school varchar(20)
);
insert into student(name,school) values('zhangsan','ningxiashifan');
insert into student(name,school) values('lisi','ningxiashifan');
然后假设实体类 User:(虽然JDBC方式跟这没关系。。。但还是先写在这里)
package domain;
public class User {
private int id;
private String name;
private String school;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public String toString() {
return "User [id=" + id + ", name=" + name + ", school=" + school + "]";
}
public User(int id, String name, String school) {
super();
this.id = id;
this.name = name;
this.school = school;
}
}
第一种:JDBC写法,需要把数据库表、表的字段写在java代码里
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
public class JdbcDemo1 {
public static void main(String[] args) throws SQLException {
//1、注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2、获取数据库连接
Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/studentdb","root","root");
//3、获取操作数据库预处理对象
PreparedStatement pstm = (PreparedStatement) conn.prepareStatement("select * from student");
//4、执行sql语句
ResultSet rs = pstm.executeQuery();
//5、遍历结果集
while (rs.next() == true) {
System.out.println(rs.getString("name")+ "," + rs.getString("school"));
}
//6、关闭资源
rs.close();
pstm.close();
conn.close();
}
}
第二种:org.apache.commons.dbutils 包
API:
commons-dbutilsAPI,相关的两个类:
- org.apache.commons.dbutils.QueryRunner
- org.apache.commons.dbutils.ResultSetHandler
一个工具类
- org.apache.commons.dbutils.DbUtils 全是静态方法
common-dbutils.jar包中,QueryRunner 类
对于QueryRunner构造方法的说明(两种构造方法,对事务的支持情况)
1、不带参数的QueryRunner构造方法。需要将来使用时再传入连接,并控制连接关闭。(支持手动事务,建议使用)
构造方法 QueryRunner() 可以不使用连接池
Constructor for QueryRunner.
这种情况下,调用update或query方法时,需要传入对应的connection参数
queryRunner.update(conn, sql,params);
conn.close();
dbutils调用这种带connection参数的update等方法时,只会关闭preparedstatement和resultset对象,不会关闭conneciton对象,所以适合手动逻辑操纵事务。
但一些情况下,没有手动关闭连接,就可能会导致连接池满了,访问数据库是处于一直等待的状态,这点需要注意。
2、带参数的QueryRunner构造方法,构造时即传入连接池对象(比如c3p0连接池),由dbutils自动控制关闭连接,每条SQL语句都是一个事务。
连接池的目的是,每次执行SQL时,从池中获取一个连接,再执行sql,最后再关闭连接。
构造方法 QueryRunner(DataSource
Constructor for QueryRunner which takes a DataSource
. DataSource对象是数据库连接池对象。
将dataSource传递进去,这样update或query方法内部就调用this.getConnection方法来从这个数据源获得连接
queryRunner.update( sql,params);
操作完后,就关闭conneciton,preparedstatement和resultset对象.
事务是自动控制的,一条SQL语句一个事务,不需要人为的控制。这种比较简单,但是需要构造时,先获取连接池。
update方法:
可执行增、删、改语句,返回值代表
|
|
|
|
|
|
|
|
|
|
|
|
int update(String sql, Object... params)
//它使用内部的连接池,每条SQL执行都是一个独立的Connection,不支持事务。
int update(Connection con, String sql, Object... parmas)
需要调用者提供Connection,这说明本方法不再使用池来管理Connection了,而是用指定的一个Connection,
可以在多次操作中,传入同一个Connection,所以只有它支持事务,但也需要自己来创建和维护连接池(如果你使用连接池的话)
比如“增”的写法: void add(); ,然后,“删” “改”的写法都差不多。
public void add() throws SQLException {
Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/studentdb","root","root"); // 获取数据库连接
QueryRunner qr = new QueryRunner();
String sql = "insert into student values(?,?,?)";
Object[] params = {3, "wangwu", "ningxiashifan"};
qr.update(conn,sql, params); //调用QueryRunner类 update方法的三参数方法
}
“查”的写法:需要使用QueryRunner类的 query方法
| |
| |
| |
| |
| |
| |
| |
|
|
列出几种查询写法:这里就需要用到实体类User了。
(1)单行结果集用法: 这里把查询到的对象信息打印出来 void find() ,查询结果是一个实体类的对象(javabean对象)
public void find() throws SQLException {
Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/studentdb","root","root"); // 获取数据库连接
QueryRunner qr = new QueryRunner();
String sql = "select * from student where id=?"; // 给出sql模板
Object[] params = {3}; // 给出参数,上面模板虽然是一个参数,但这里用的是数组方式
// 接下来执行query()方法,需要给出结果集处理器,即ResultSetHandler的实现类对象
// 我们用的是org.apache.commons.dbutils.handlers.BeanHandler;它实现了ResultSetHandler;
// 它需要一个类型,然后它会把rs中的数据封装到指定类型的一个javabean对象中,然后返回。
User user = qr.query(sql, new BeanHandler<User>(User.class), params);
System.out.println(user);
conn.close();//别忘了关连接
}
(2)多行结果集用法: void findAll()
/**
* BeanListHandler的应用,它是多行处理器
* 每行对象一个User对象!
* @throws Exception
*/
@Test
public void findAll() throws Exception {
Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/studentdb","root","root"); // 获取数据库连接
QueryRunner qr = new QueryRunner();
String sql = "select * from student";
List<User> userList = qr.query(sql, new BeanListHandler<User>(User.class));//BeanListHandler类用于查询结果是多行,返回一个List<T>
System.out.println(userList);//或增强型for循环去遍历List
conn.close();
}
(3)结果集不仅可以是实体类、实体类的List,还可以是map对象,和List<map<key,value>>;
/**
* MapHandler的应用,它是单行处理器,把一行转换成一个Map对象
* @throws SQLException
*/
@Test
public void find() throws SQLException {
Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/studentdb","root","root"); // 获取数据库连接
QueryRunner qr = new QueryRunner();
String sql = "select * from student where id=?";
Object[] params = {3};
Map map = qr.query(sql, new MapHandler(), params);
System.out.println(map);
conn.close();
}
/**
* MapListHandler,它是多行处理器,把每行都转换成一个Map,即List<Map>
* @throws SQLException
*/
@Test
public void findAll() throws SQLException {
Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/studentdb","root","root"); // 获取数据库连接
QueryRunner qr = new QueryRunner();
String sql = "select * from student";
List<Map<String,Object>> mapList = qr.query(sql, new MapListHandler());
System.out.println(mapList); //可以使用增强型for循环遍历List
conn.close();
}
(4) 查询的结果集是单行单列的结果,比如select count(*) 的结果
/**
* ScalarHandler,它是单行单列时使用,最为合适!
* @throws SQLException
*/
@Test
public void find() throws SQLException {
Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/studentdb","root","root"); // 获取数据库连接
QueryRunner qr = new QueryRunner();
String sql = "select count(*) from student";
/*
* Integer、Long、BigInteger
*/
Number cnt = (Number)qr.query(sql, new ScalarHandler());
//不同数据库对于count()函数的返回值类型定义不同,如果更换驱动,比如到了Oracle,可能是BigInteger类型
//mySql是 Long类型,不管是Integer、BigInteger、Long类型,都有共同顶层类Number型。
long c = cnt.longValue();
System.out.println(c);
conn.close();
}
}
第三种:Mybatis用法
基于XML文件.在类路径(eclipse的src路径,或者IDEA的java、resource路径)下,首先需要有一个Mybatis的配置文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- XML 配置文件包含对 MyBatis 系统的核心设置 -->
<configuration>
<environments default="mysql">
<!-- 环境配置,即连接的数据库。 -->
<environment id="mysql">
<!-- 指定事务管理类型,type="JDBC"指直接简单使用了JDBC的提交和回滚设置 -->
<transactionManager type="JDBC"/>
<!-- dataSource指数据源配置,POOLED是JDBC连接对象的数据源连接池的实现。 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/studentdb"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- mappers告诉了MyBatis去哪里找持久化类的映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
再需要一个对应实体类的映射文件,在mapper包下的 UserMapper.xml ,在上面的基础配置文件里,需要声明出来。
实体类User(POJO),还需要对应的接口mapper.UserMapper,接口的名字与映射文件名相同,这里是接口UserMapper.java
package mapper;
import java.util.List;
import domain.User;
public interface UserMapper {
public User selectUser(int id); //查到User对象
public void saveUser(User user); //增
public void updateUser(User user); //改
public void deleteUser(int id); //删
public List<User> selectAll(); //查到List<User>
}
演示:“查”的写法,在接口中书写方法 User selectUser(int id);
SQL返回的列名,要与上面的POJO类的属性对应起来,MyBatis会自动对他们做映射。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace指用户自定义的命名空间。 -->
<mapper namespace="mapper.UserMapper">
<!--"增"
id="save"是唯一的标示符
parameterType属性指明插入时使用的参数类型
useGeneratedKeys="true"表示使用数据库的自动增长策略
-->
<insert id="saveUser" parameterType="domain.User" useGeneratedKeys="true">
INSERT INTO student(id,name,school)
VALUES(#{id},#{name},#{school})
</insert>
<!-- "查",查到一个User对象
parameterType="int"表示该sql语句中的模板参数#{}类型是int
resultType 表示返回值的类型-->
<select id="selectUser" parameterType="int" resultType="domain.User">
SELECT * FROM student WHERE id = #{id}
</select>
<!-- "查",查到一个List<User>列表-->
<select id="selectAll" resultType="domain.User">
SELECT * FROM student
</select>
<!-- "改"操作-->
<update id="updateUser" parameterType="domain.User">
UPDATE student
SET id = #{id}, name = #{name}, school = #{school}
WHERE id = #{id}
</update>
<!-- "删"操作 -->
<delete id="deleteUser" parameterType="int">
DELETE FROM student WHERE id = #{id}
</delete>
</mapper>
测试查一个User:
package test;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import domain.User;
import mapper.UserMapper;
public class TestSelect {
public static void main(String[] args) throws Exception {
SqlSessionFactory sqlSessionFactory = null;
String resource = "mybatis-config.xml";
InputStream is;
try {
is = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
SqlSession sqlSession = null;
try{
sqlSession = sqlSessionFactory.openSession();//打开会话
UserMapper mapper = sqlSession.getMapper(UserMapper.class);//获取mapper
User user = mapper.selectUser(1); //sql查询逻辑,返回一个User对象
System.out.println(user); //看能否能查询到返回值对象
sqlSession.commit();//提交事务
}catch(Exception e){
sqlSession.rollback();//回滚事务
}finally{
//打开会话失败时,sqlSession会是null,此时不需要进行关闭
if(sqlSession != null)
sqlSession.close();
}
}
}
//控制台输出:User [id=1, name=zhangsan, school=ningxiashifan]
测试删除一个User对象
package test;
//省略了跟上面一样.....
public class TestDelete {
public static void main(String[] args) throws Exception {
//...跟上面一样
UserMapper mapper = sqlSession.getMapper(UserMapper.class);//获取mapper
mapper.deleteUser(2); //SQL删除业务mybatis代码
sqlSession.commit();//提交事务
//....跟上面一样
}
}
//进mysql命令查看,确实删除成功。
测试增加一个User对象
package test;
//省略。。。跟上面一样
public class TestSave {
public static void main(String[] args) throws Exception {
//省略。。。跟上面一样
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.saveUser(new User(2, "zhaoliu", "ningxiashifan")); //mybatis增加User对象业务的代码
sqlSession.commit();//提交事务
//省略。。。跟上面一样
}
}
//再次运行TestSelect ,查id=2可以看到控制台:User [id=2, name=zhaoliu, school=ningxiashifan]
改动一个User对象测试:
package test;
//省略。。。跟上面一样
public class TestUpdate {
public static void main(String[] args) throws Exception {
//省略。。。跟上面一样
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User(2, "wangwu", "ningxiashifan"); //创建一个对象,以便用于更新
mapper.updateUser(user);//修改一个User对象的mybatis业务代码
sqlSession.commit();//提交事务
//省略。。。跟上面一样
}
}
//注意,改动时,不要超过原始mysql的varchar的数据长度。
//再次运行TestSelect ,id=2,控制台看到 User [id=2, name=wangwu, school=ningxiashifan]
查询全部User对象得到List<User>测试代码:
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectAll();
for (User user : userList) {
System.out.println(user);
}
sqlSession.commit();