编写DAO,通过JdbcTemplate操作数据库的实践
目的:编写DAO,通过Spring中的JdbcTemplate,对数据库中的学生数据进行增删改查操作。
要操作的数据库表结构为:
一、大体框架
1.要利用JdbcTemplate,首先要添加Spring依赖。用quickstart模板创建Maven项目,在pom.xml中添加Spring依赖:
2.创建学生类(数据传递类),描述学生信息。
3.创建数据操作接口,规定需要哪些方法来操作数据。
增、改各一种方法,删、查各4中方法:通过id,name,专业+学号,Student对象(方便出现前三种方法以外的需求时删、查)。
4.创建数据操作类,实现数据操作接口
5.创建DAO工厂类,用于创建数据操作对象(日后有新的数据操作类时,只需在此修改)
6.测试类
二、通过JdbcTemplate操作数据库
为了让JdbcTemplate正常工作,只需要为其设置DataSource就可以了。
1.数据源DataSource
(1)基于Jdbc驱动的数据源
Spring提供了三个通过Jdbc驱动定义数据源的数据源类(org.springframework.jdbc.datasource.*):
- DriverManagerDataSource类:每次连接请求时返回一个新的链接。
- SimpleDriverDataSource类:同上,但直接使用Jdbc驱动,未解决特定环境下的类加载问题。
- SingleConnectionDataSource类:每次连接请求时返回同一个链接。可以看作只有一个链接的连接池。
【这三个数据源类一般用于测试,项目上用数据源连接池是更好的选择】
配置方法:
①Java程序中配置:【例】
//数据库配置存放在配置文件db.properties中 //载入db.properties
Properties sqlConfig = new Properties();
sqlConfig.load(new FileInputStream("db.properties")); //抛出IOException(FileNotFoundException)
//不抛出Exception的方法:
//sqlConfig.load(本类类名.getClassLoader().getResourceAsStream("db.properties")); //读取db.properties中的数据
String driver = sqlConfig.getProperty("driver");
String url = sqlConfig.getProperty("url");
String username = sqlConfig.getProperty("username");
String password = sqlConfig.getProperty("password"); //配置数据源对象
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
②xml文件中配置
<context:property-placeholder location="classpath:db.properties">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${driver}"
p:url="${url}"
p:username="${username}"
p:password="${password}" />
(2)数据源连接池
Spring中并没有提供数据源连接池的实现,但我们可以利用开源实现:
- Apache Commons DBCP:http://jakarta.apache.org/commons/dbcp
- c3p0:http://sourceforge.NET/projects/c3p0/
- BoneCP:http://jolbox.com/
这些数据源的配置方法与(1)中基于Jdbc的数据源类似:
①Java程序中配置:【以DBCP为例】
在Maven中添加依赖后:
(前面的获取db.properties中数据的方法相同,此处不再赘述)
//配置数据源对象
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setInitialSize(5); //池配置属性
ds.setMaxActive(10);
BasicDataSource中的池配置属性:
②xml文件中配置
<context:property-placeholder location="classpath:db.properties">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${driver}"
p:url="${url}"
p:username="${username}"
p:password="${password}"
p:initialSize="5"
p:maxActive="10" />
2.创建JdbcTemplate对象(将DataSource注入到jdbcTemplate实例)
(1)Java程序中完成
通过1中程序获取dataSource实例后:
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
这里的dataSource可以是javax.sql.DataSource的任意实现。
(2)xml文件完成
通过Spring的依赖注入,将dataSource注入到jdbcTemplate中:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
然后在程序中通过getBean获取这个jdbcTemplate:
ApplicationContext apc = new ClassPathXmlApplicationContext("xxx.xml");
JdbcTemplate jdbcTemplate = (JdbcTemplate) apc.getBean("jdbcTemplate");
3.使用JdbcTemplate中的方法操作数据
在调用相关函数时,将sql语句中要操作的值写为?,然后在后面加上对应的值作为参数。如果是单个值,可直接作为参数传递;如果是多个值,可以用参数数组的形式传递:new Object[]{值1,值2,...}。
比如:
要插入一个name数据,可以这样写:
jdbcTemplate.update("INSERT INTO student (name) VALUES (?)","张三");
要修改姓名和qq,可以这样写:
jdbcTemplate.update("UPDATE student SET name=?,qq=? WHERE id=?",new Object[]{"李四","123456789",45});
值得一提的是,程序中的sql语句并不需要写分号作为结尾。
JdbcTemplate中的常用方法:
- excute(sql):可以用于执行任何sql语句,一般用于执行DDL语句(数据定义语言,用于定义、修改、删除数据库/表)【返回:void】
- update(sql,Object[]):执行增删改sql语句【返回:int,影响的行数】
- batchUpdate(sql,List<Object[]>):执行批处理增删改sql语句【返回:int[],受每个语句影响的行数】
- query(sql,RowMapper<T>,Object)或(sql,Object[],RowMapper<T>):执行sql查询语句,通过RowMapper将每个行映射到一个java对象。【返回:List<T>,结果列表(映射对象)】
- queryForObject(sql,RowMapper<T>,Object)或(sql,Object[],RowMapper<T>):执行sql查询语句,通过RowMapper将单个结果映射到java对象。【返回:T,映射对象】
- Spring:spring-context,spring-core,spring-jdbc,spring-tx
- 数据源:DBCP
- Mysql驱动
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.4</version>
</dependency>
</dependencies>
pom.xml中的dependencies
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/students?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username=root
password=pass
initialSize=3
maxActive=10
db.properties
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="location">
<list>
<value>classpath:db.properties</value>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
applicationContext.xml:这里的头文件使用Spring Tool Suite生成的。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">
<!--<context:property-placeholder location="classpath:db.properties"/>-->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:db.properties"></property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
<property name="url" value="${url}"></property>
<property name="driverClassName" value="${driver}"></property>
<property name="initialSize" value="${initialSize}"></property>
<property name="maxActive" value="${maxActive}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--<bean id="jdbcTemplate" class="cn.cage.student.StudentDAOImpl">-->
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
applicationContext.xml
3.创建Student类,用于描述学生
修改:
* 1.添加hashCode和equals方法
* 2.将构造方法获取的参数由int改为Interger,否则在Mybatis查询中,会出现“找不到类型为[String,String,Integer]的构造函数”的报错。
/**
* @FileName:Student.java
* @description:
* @author Cage Yang
* @version
* Modified Date:2017年7月27日
* Why & What is modified:
* 1.添加hashCode和equals方法
* 2.将构造方法获取的参数由int改为Interger,否则在Mybatis查询中,会出现“找不到类型为[String,String,Integer]的构造函数”的报错。
*/
package cn.cage.student; /**
* @ClassName Student
* @description 描述学生信息的类。
* @author Cage Yang
*/
public class Student implements Comparable<Student> {
private long id;
private long createTime;
private long updateTime;
private String name;
private String qq;
private String major; //学习方向
private String entryTime;
private String school;
private int jnshuId; //修真院ID
private String dailyUrl;
private String desire;
private String jnshuBro;//修真院师兄
private String knowFrom;//信息来源
/**
* @param name 学生姓名
* @param major 学习方向
* @param jnshuId 修真院ID
*/
public Student(String name, String major, Integer jnshuId) {
this.name = name;
this.major = major;
this.jnshuId = jnshuId;
}
/* (非 Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Student [name=" + name + ", major=" + major + ", jnshuId=" + jnshuId + "]";
}
/* (非 Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + jnshuId;
result = prime * result + ((major == null) ? 0 : major.hashCode());
return result;
}
/* (非 Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Student)) {
return false;
}
Student other = (Student) obj;
if (jnshuId != other.jnshuId) {
return false;
}
if (major == null) {
if (other.major != null) {
return false;
}
} else if (!major.equals(other.major)) {
return false;
}
return true;
}
/* (非 Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Student o) {
// TODO 自动生成的方法存根
if (this.id!=o.id) { //其实相等就是指二者id都不存在(为0)的情况
return (new Long(this.id)).compareTo(new Long(o.id));
}
if (!this.major.equals(o.major)) {
return this.major.compareTo(o.major);
}
return (new Integer(this.jnshuId).compareTo(new Integer(o.jnshuId)));
}
/**
* @return id
*/
public long getId() {
return id;
}
/**
* @param id 要设置的 id
*/
public void setId(long id) {
this.id = id;
}
/**
* @return name
*/
public String getName() {
return name;
}
/**
* @param name 要设置的 name
*/
public void setName(String name) {
this.name = name;
}
/**
* @return qq
*/
public String getQq() {
return qq;
}
/**
* @param qq 要设置的 qq
*/
public void setQq(String qq) {
this.qq = qq;
}
/**
* @return major
*/
public String getMajor() {
return major;
}
/**
* @param major 要设置的 major
*/
public void setMajor(String major) {
this.major = major;
}
/**
* @return entryTime
*/
public String getEntryTime() {
return entryTime;
}
/**
* @param entryTime 要设置的 entryTime
*/
public void setEntryTime(String entryTime) {
this.entryTime = entryTime;
}
/**
* @return school
*/
public String getSchool() {
return school;
}
/**
* @param school 要设置的 school
*/
public void setSchool(String school) {
this.school = school;
}
/**
* @return jnshuId
*/
public int getJnshuId() {
return jnshuId;
}
/**
* @param jnshuId 要设置的 jnshuId
*/
public void setJnshuId(int jnshuId) {
this.jnshuId = jnshuId;
}
/**
* @return dailyUrl
*/
public String getDailyUrl() {
return dailyUrl;
}
/**
* @param dailyUrl 要设置的 dailyUrl
*/
public void setDailyUrl(String dailyUrl) {
this.dailyUrl = dailyUrl;
}
/**
* @return desire
*/
public String getDesire() {
return desire;
}
/**
* @param desire 要设置的 desire
*/
public void setDesire(String desire) {
this.desire = desire;
}
/**
* @return jnshuBro
*/
public String getJnshuBro() {
return jnshuBro;
}
/**
* @param jnshuBro 要设置的 jnshuBro
*/
public void setJnshuBro(String jnshuBro) {
this.jnshuBro = jnshuBro;
}
/**
* @return knowFrom
*/
public String getKnowFrom() {
return knowFrom;
}
/**
* @param knowFrom 要设置的 knowFrom
*/
public void setKnowFrom(String knowFrom) {
this.knowFrom = knowFrom;
}
/**
* @return createTime
*/
public long getCreateTime() {
return createTime;
}
/**
* @return updateTime
*/
public long getUpdateTime() {
return updateTime;
}
/**
* @param createTime 要设置的 createTime
*/
public void setCreateTime(long createTime) {
this.createTime = createTime;
}
/**
* @param updateTime 要设置的 updateTime
*/
public void setUpdateTime(long updateTime) {
this.updateTime = updateTime;
} }
POJO类:Student
4.创建数据传递接口,规定操作数据的方法
* 原因:不需要获取旧学生信息;由调用者提供id参数更好。
* 2.修改queryStuByName的返回值为List<Student>,因为可能出现的同名学生。
* 3.修改delStuByName的返回值为int,因为可能出现的同名学生。
* 4.删除delStuByName和delStuByJnshu两个方法。
* 原因:在实际业务逻辑中一般是先查询,然后根据查询结果选择删除。既然是这样,只需要根据选择项的id删除即可,不会出现误删的问题。
/**
* @FileName:DataOperate.java
* @description:
* @author Cage Yang
* @version
* Modified Date:2017年8月4日
* Why & What is modified:
* 1.将updateStu函数的返回值由Student修改为boolean,参数列表增加long id。
* 原因:不需要获取旧学生信息;由调用者提供id参数更好。
* 2.修改queryStuByName的返回值为List<Student>,因为可能出现的同名学生。
* 3.修改delStuByName的返回值为int,因为可能出现的同名学生。
* 4.删除delStuByName和delStuByJnshu两个方法。
* 原因:在实际业务逻辑中一般是先查询,然后根据查询结果选择删除。既然是这样,只需要根据选择项的id删除即可,不会出现误删的问题。 */
package cn.cage.student; import java.util.List; /**
* @ClassName DataOperate
* @description 数据操作接口,规定了对学生对象数据的增删改查四种操作。
* @author Cage Yang
*/
public interface StudentDAO {
//增
public abstract boolean addStu(Student stu); //删
public abstract boolean delStuById(long id);
// public abstract int delStuByName(String name);
// public abstract boolean delStuByJnshu(String major, int jnshuId);
public abstract boolean delStu(Student stu); //改
public abstract boolean updateStu(Student stu, long id); //查
public abstract Student queryStuById(long id);
public abstract List<Student> queryStuByName(String name);
public abstract Student queryStuByJnshu(String major, int jnshuId);
public abstract Student queryStu(Student stu);
}
DAO接口
5.创建数据传递类,实现数据操作
* 1.将updateStu函数的返回值由Student修改为boolean,参数列表增加long id。
* 函数体:将返回值设为“影响行数>0则返回true”;sql语句中的条件参数由stu.getId()改为id。
* 原因:不需要获取旧学生信息;由调用者提供id参数更好。
* 2.在addStu函数中,去掉sql语句中id的添加,id由数据库自增完成,以免发生id重复的情况。
* 3.将各查询方法中的RowMapper提取出来封装为一个类,在各查询方法中直接使用此类对象,避免重复代码。
* 为适配封装的QueryStuRowMapper,将各查询方法中的sql语句改为查询所有属性。
* 4.修改addStu函数体,当name、major、jnshuId为空时报错。
* 5.在updateStu函数体中也加上非空报错。
* 6.bug:各sql语句中的表名是student,数据库表名是students,应修改。
* 7.在通过id、专业+学号查询的方法中,通过try-catch捕获EmptyResultDataAccessException异常,给出查询结果为空的提示。
* 8.修改queryStuByName的返回值为List<Student>,因为可能出现的同名学生;然后通过query函数查询,且当结果size==0时,给出查询结果为空的提示。
* 9.修改delStuByName的返回值为int,因为可能出现的同名学生。
* 10.删除delStuByName和delStuByJnshu两个方法。
* 原因:在实际业务逻辑中一般是先查询,然后根据查询结果选择删除。既然是这样,只需要根据选择项的id删除即可,不会出现误删的问题。
/**
* @FileName:StudentDAOSpringImpl.java
* @description:
* @author Cage Yang
* @version
* Modified Date:2017年8月4日
* Why & What is modified:
* 1.将updateStu函数的返回值由Student修改为boolean,参数列表增加long id。
* 函数体:将返回值设为“影响行数>0则返回true”;sql语句中的条件参数由stu.getId()改为id。
* 原因:不需要获取旧学生信息;由调用者提供id参数更好。
* 2.在addStu函数中,去掉sql语句中id的添加,id由数据库自增完成,以免发生id重复的情况。
* 3.将各查询方法中的RowMapper提取出来封装为一个类,在各查询方法中直接使用此类对象,避免重复代码。
* 为适配封装的QueryStuRowMapper,将各查询方法中的sql语句改为查询所有属性。
* 4.修改addStu函数体,当name、major、jnshuId为空时报错。
* 5.在updateStu函数体中也加上非空报错。
* 6.bug:各sql语句中的表名是student,数据库表名是students,应修改。
* 7.在通过id、专业+学号查询的方法中,通过try-catch捕获EmptyResultDataAccessException异常,给出查询结果为空的提示。
* 8.修改queryStuByName的返回值为List<Student>,因为可能出现的同名学生;然后通过query函数查询,且当结果size==0时,给出查询结果为空的提示。
* 9.修改delStuByName的返回值为int,因为可能出现的同名学生。
* 10.删除delStuByName和delStuByJnshu两个方法。
* 原因:在实际业务逻辑中一般是先查询,然后根据查询结果选择删除。既然是这样,只需要根据选择项的id删除即可,不会出现误删的问题。
*/
package cn.cage.student; import java.util.List; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; /**
* @ClassName StudentDAOSpringImpl
* @description
* @author Cage Yang
*/
public class StudentDAOSpringImpl implements StudentDAO {
private ApplicationContext apc = null;
private JdbcTemplate jdbcTemplate = null; /**
* 创建对象的同时获取jdbcTemplate对象。
*/
public StudentDAOSpringImpl() {
// TODO 自动生成的构造函数存根
apc = new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate = (JdbcTemplate) apc.getBean("jdbcTemplate");
}
/* (非 Javadoc)
* @see cn.cage.student.StudentDAO#addStu(cn.cage.student.Student)
*/
public boolean addStu(Student stu) {
// TODO 自动生成的方法存根
String name = stu.getName(), major = stu.getMajor();
int jnshuId = stu.getJnshuId();
if (name==null || major==null || jnshuId==0) {
throw new RuntimeException("addStu:姓名、专业、学号不能为空!");
}
String sql = "INSERT INTO students (name,qq,major,entrytime,gra_school,id_jnshu"
+ ",daily_url,desire,bro_jnshu,knowfrom) VALUES (?,?,?,?,?,?,?,?,?,?)";
int line = jdbcTemplate.update(sql,new Object[] {
name,stu.getQq(),major,stu.getEntryTime(),
stu.getSchool(),jnshuId,stu.getDailyUrl(),stu.getDesire(),
stu.getJnshuBro(),stu.getKnowFrom()});
return line>0?true:false;
} /* (非 Javadoc)
* @see cn.cage.student.StudentDAO#delStuById(long)
*/
public boolean delStuById(long id) {
// TODO 自动生成的方法存根
String sql = "DELETE FROM students WHERE id=?";
int line = jdbcTemplate.update(sql,id);
return line>0?true:false;
} /* (非 Javadoc)
* @see cn.cage.student.StudentDAO#delStuByName(java.lang.String)
*/
/*
public int delStuByName(String name) {
// TODO 自动生成的方法存根
String sql = "DELETE FROM students WHERE name=?";
return jdbcTemplate.update(sql,name);
}
*/
/* (非 Javadoc)
* @see cn.cage.student.StudentDAO#delStuByJnshu(java.lang.String, int)
*/
/*
public boolean delStuByJnshu(String major, int jnshuId) {
// TODO 自动生成的方法存根
String sql = "DELETE FROM students WHERE id_jnshu=? and major=?";
int line = jdbcTemplate.update(sql,new Object[] {jnshuId,major});
return line>0?true:false;
}
*/
/* (非 Javadoc)
* 用于日后需要添加通过其他元素删除学生记录时的情况
* @see cn.cage.student.StudentDAO#delStu(cn.cage.student.Student)
*/
public boolean delStu(Student stu) {
// TODO 自动生成的方法存根
return false;
} /* (非 Javadoc)
* @see cn.cage.student.StudentDAO#updateStu(cn.cage.student.Student)
*/
public boolean updateStu(Student stu, long id) {
// TODO 自动生成的方法存根
String name = stu.getName(), major = stu.getMajor();
int jnshuId = stu.getJnshuId();
if (name==null || major==null || jnshuId==0) {
throw new RuntimeException("updateStu:姓名、专业、学号不能为空!");
}
String sql = "UPDATE students SET name=?,qq=?,major=?,entrytime=?,gra_school=?,id_jnshu=?"
+ ",daily_url=?,desire=?,bro_jnshu=?,knowfrom=? WHERE id=?";
int line = jdbcTemplate.update(sql,new Object[]{
name,stu.getQq(),major,stu.getEntryTime(),
stu.getSchool(),jnshuId,stu.getDailyUrl(),stu.getDesire(),
stu.getJnshuBro(),stu.getKnowFrom(),id});
return line>0?true:false;
} /* (非 Javadoc)
* @see cn.cage.student.StudentDAO#queryStuById(int)
*/
public Student queryStuById(long id) {
// TODO 自动生成的方法存根
String sql = "SELECT id,create_at,update_at,name,qq,major,entrytime,gra_school" +
",id_jnshu,daily_url,desire,bro_jnshu,knowfrom FROM students WHERE id=?";
Student stu = null;
try {
stu = jdbcTemplate.queryForObject(sql, new QueryStuRowMapper(),id);
} catch (EmptyResultDataAccessException e) {
// TODO 此处可做更复杂的提示动作,比如抛出异常、记录到本地文件、显示到GUI等。
System.out.println("queryStuById:该学生不存在!");
}
return stu;
} /* (非 Javadoc)
* @see cn.cage.student.StudentDAO#queryStuByName(java.lang.String)
*/
public List<Student> queryStuByName(String name) {
// TODO 自动生成的方法存根
String sql = "SELECT id,create_at,update_at,name,qq,major,entrytime,gra_school" +
",id_jnshu,daily_url,desire,bro_jnshu,knowfrom FROM students WHERE name=?";
List<Student> list = jdbcTemplate.query(sql, new QueryStuRowMapper(),name);
if (list.size()==0) {
System.out.println("queryStuByName:该学生不存在!");
}
return list;
} /* (非 Javadoc)
* @see cn.cage.student.StudentDAO#queryStuByJnshu(java.lang.String, int)
*/
public Student queryStuByJnshu(String major, int jnshuId) {
// TODO 自动生成的方法存根
String sql = "SELECT id,create_at,update_at,name,qq,major,entrytime,gra_school,id_jnshu" +
",daily_url,desire,bro_jnshu,knowfrom FROM students WHERE id_jnshu=? and major=?";
Student stu = null;
try {
stu = jdbcTemplate.queryForObject(sql, new QueryStuRowMapper(),new Object[]{jnshuId,major});
} catch (EmptyResultDataAccessException e) {
// TODO 此处可做更复杂的提示动作,比如抛出异常、记录到本地文件、显示到GUI等。
System.out.println("queryStuByJnshu:该学生不存在!");
}
return stu;
} /* (非 Javadoc)
* 用于日后需要添加通过其他元素查询学生记录时的情况
* @see cn.cage.student.StudentDAO#queryStu(cn.cage.student.Student)
*/
public Student queryStu(Student stu) {
// TODO 自动生成的方法存根
return null;
} }
DAO接口的实现类
提取出的RowMapper实现类:
注:从MySQL中获取的Date值,在java中是java.sql.Date,toString的格式为YYYY-MM-dd。
修改:
* 1.给entrytime的设定语句加上非空判断,否则当其为空时,null.toString抛出空指针异常。
/**
* @FileName:QueryStuRowMapper.java
* @description:
* @author Cage Yang
* @version
* Modified Date:2017年8月4日
* Why & What is modified:
* 1.给entrytime的设定语句加上非空判断,否则当其为空时,null.toString抛出空指针异常。
*/
package cn.cage.student; import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException; import org.springframework.jdbc.core.RowMapper; /**
* @ClassName QueryStuRowMapper
* @description 直接使用此类建立的对象时,sql语句应查询Student的所有属性。
* @author Cage Yang
*/
public class QueryStuRowMapper implements RowMapper<Student> {
/**
* 直接使用此类建立的对象时,sql语句应查询Student的所有属性。
* @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
*/
public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
// TODO 自动生成的方法存根
Student stu = new Student(rs.getString("name"), rs.getString("major"), rs.getInt("id_jnshu"));
stu.setId(rs.getLong("id"));
stu.setCreateTime(rs.getLong("create_at"));
stu.setUpdateTime(rs.getLong("update_at"));
stu.setQq(rs.getString("qq"));
// TODO 从Mysql中获取的Date值,在java中是java.sql.Date,toString的格式为YYYY-MM-dd。
Date entryTime = rs.getDate("entrytime");
if (entryTime!=null) {
stu.setEntryTime(entryTime.toString());
}
stu.setSchool(rs.getString("gra_school"));
stu.setDailyUrl(rs.getString("daily_url"));
stu.setDesire(rs.getString("desire"));
stu.setJnshuBro(rs.getString("bro_jnshu"));
stu.setKnowFrom(rs.getString("knowfrom"));
return stu;
}
}
查询方法的RowMapper
6.创建DAO工厂类,提供Impl对象
/**
* @FileName:StudentDAO.java
* @description:
* @author Cage Yang
* @version
* Modified Date:2017年7月27日
* Why & What is modified: <修改原因描述>
*/
package cn.cage.student; /**
* @ClassName StudentDAO
* @description DAO工厂类,用于创建数据操作对象。
* @author Cage Yang
*/
public class DAOFactory {
public static StudentDAO getDAOImpl() {
return new StudentDAOSpringImpl();
}
}
DAO工厂
7.测试类
错误原因:a.数据库名是students,少了个s;b.是正斜杠而不是反斜杠
bug2:Access denied for user 'asus'@'localhost' (using password: YES)
错误定位:db.properties读取(<context:property-placeholder location="classpath:db.properties"/>)发生错误。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:db.properties"></property>
</bean>
错误原因:各sql语句中插入的表名是student,应该为students
bug4:Column 'jnshuId' not found.
错误原因:mapRow中的rs.getString("")引号中应为数据库的列名,而不是Student类中的属性名 。
bug5:打印结果为数组地址
修改:将showStu函数中的sop(obj)改为foreach循环遍历。
Warn:Sat Aug 05 03:09:59 CST 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
原因:未设置数据库的连接属性(是否使用SSL)。
Student stu = test.stuDAO.queryStuByName("胡凯博");
test.showStu(stu);
Student stu = test.stuDAO.queryStuByName("胡凯博");
test.showStu(stu);
结果:
原因:返回了大量同名的学生。
- 各查询函数中,添加对要查询的学生不存在的情况的处理
- 通过name查询学生时,返回值改为List<Student>,因为可能有重名学生
- 通过name删除学生时,返回值改为int,因为可能删除多名学生
所以,在通过id、专业+学号查询时,通过try-catch捕获异常,给出查询结果为空的提示;通过name查询时,改为调用query方法。
/**
* @FileName:StuDaoImplTest.java
* @description:
* @author Cage Yang
* @version
* Modified Date:2017年8月8日
* Why & What is modified: <修改原因描述>
*/
package cn.cage.student; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import java.util.Iterator;
import java.util.List; import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test; /**
* @ClassName StuDaoImplTest
* @description
* @author Cage Yang
*/
public class StuDaoImplTest {
private static StudentDAOSpringImpl stuDao = null;
/**
* @description
* @throws java.lang.Exception
*/
@BeforeClass
public static void setUpBeforeClass() throws Exception {
stuDao = new StudentDAOSpringImpl();
} @AfterClass
public static void tearDownAfterClass() throws Exception {
stuDao = null;
} /**
* {@link cn.cage.student.StudentDAOSpringImpl#addStu(cn.cage.student.Student)} 的测试方法。
*/
@Test
public void testAddStu() {
Student stu = RandomStudent.getStudent();
stu.setEntryTime("1995-8-6");
assertTrue("插入失败!",stuDao.addStu(stu));
assertEquals("插入错误,或查询byJnshu出错", stu, stuDao.queryStuByJnshu("Java后端工程师", 1501));
} /**
* {@link cn.cage.student.StudentDAOSpringImpl#delStuById(long)} 的测试方法。
*/
@Test
public void testDelStuById() {
assertTrue("删除失败!",stuDao.delStuById(3));
assertNull("删除错误,或查询byId出错", stuDao.queryStuById(3));
} /**
* {@link cn.cage.student.StudentDAOSpringImpl#updateStu(cn.cage.student.Student, long)} 的测试方法。
*/
@Test
public void testUpdateStu() {
Student stu = RandomStudent.getStudent();
stu.setDesire("哈哈哈哈哈哈哈哈");
assertTrue("更新失败!", stuDao.updateStu(stu, 2));
assertEquals("更新错误,或查询byId出错", "哈哈哈哈哈哈哈哈", stuDao.queryStuById(2).getDesire());
} /**
* {@link cn.cage.student.StudentDAOSpringImpl#queryStuByName(java.lang.String)} 的测试方法。
*/
@Test
public void testQueryStuByName() {
List<Student> list = stuDao.queryStuByName("王五");
for (Iterator<Student> iterator = list.iterator(); iterator.hasNext();) {
Student student = (Student) iterator.next();
if (student.getJnshuId()==1509) {
assertEquals("查询byName出错", "2017-08-06", student.getEntryTime());
}
}
}
}
JUnit4单元测试
②测试结果
- 即使不存在安全性问题,MySQL也应该设定密码。否则可能出现程序无法连接到数据库的情况。
- POJO类中,尽量做到各属性名称、数据类型都和数据库中相同,这样可以免去很多麻烦。
- POJO类都要自定义toString、hashCode、equals、compareTo方法。特别是前三者,比较两个实例是否相同是一个很常见的操作,如果不自定义,根本无法比较。compareTo是考虑到实例可能存入TreeSet,最好自定义;不过即使不自定义,也可通过比较器来实现比较功能。
- 对于DAO接口及其实现类,增删改最好返回int型(影响行数);在insert、update数据时,要考虑到数据库设计时加了非空约束的列,对应属性为空值时直接抛出异常,不向数据库提交数据;delete方法一般只需要一个(比如id)即可,因为正常逻辑是先查询到要删除的数据,在根据查询结果中对应的id删除即可;在select数据时,要考虑到查询值不存在、查询值不唯一的情况,做出应对,避免抛出意料外的异常。
- 学习了JUnit4单元测试的编写与使用。
编写DAO,通过JdbcTemplate操作数据库的实践的更多相关文章
- 170623、springboot编程之JdbcTemplate操作数据库
使用JdbcTemplate操作mysql数据库! 1.在pom中引入jpa包 <dependency> <groupId>org.springframework.boot&l ...
- 使用JdbcTemplate操作数据库(二十九)
使用JdbcTemplate操作数据库 Spring的JdbcTemplate是自动配置的,你可以直接使用@Autowired来注入到你自己的bean中来使用. 举例:我们在创建User表,包含属性n ...
- Spring Boot教程(二十九)使用JdbcTemplate操作数据库
使用JdbcTemplate操作数据库 Spring的JdbcTemplate是自动配置的,你可以直接使用@Autowired来注入到你自己的bean中来使用. 举例:我们在创建User表,包含属性n ...
- JdbcTemplate操作数据库
1.JdbcTemplate操作数据库 Spring对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate之中.同时,为了支 ...
- Spring Boot入门系列(十四)使用JdbcTemplate操作数据库,配置多数据源!
前面介绍了Spring Boot 中的整合Mybatis并实现增删改查.如何实现事物控制.不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/c ...
- Spring4.3.1 JDBCTemplate操作数据库
个人总结,转载请注明出处:http://www.cnblogs.com/lidabnu/p/5679354.html 基于Spring4.3.1官方文档总结,官方文档链接http://docs.spr ...
- spring-boot-route(七)整合jdbcTemplate操作数据库
在一部分内容中,我们学习了Restful接口的编写,及接口文档的生成.我们需要将接口数据进行持久化存储,这一部分我们主要学习几种持久化框架将数据进行存储.本部分内容中,我们都将使用mysql为例来做为 ...
- Spring4.0学习笔记(12) —— JDBCTemplate 操作数据库
整体配置 1.配置xml文件 <beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi ...
- 07_数据库创建,添加c3p0操作所需的jar包,编写c3p0-config.xml文件,编写User.java,编写jdbcUtils.java实现操作数据库的模板工具类,UserDao编写,Dao
1 创建day14数据库,创建user.sql表: A 创建数据库 day14 B 创建数据表 users create table users ( id int primary keyaut ...
随机推荐
- 06_Ajax初步入门第一天
视频来源:麦子学院 讲师:李毅 ajax:异步JavaScript和XML,局部刷新 原生ajax实例 创建对象 XMLHttpRequest对象 request=new XMLHttpRequest ...
- 2018-01-28-TF源码做版本兼容的一个粗暴方法
layout: post title: 2018-01-28-TF源码做版本兼容的一个粗暴方法 key: 20180128 tags: IT AI TF modify_date: 2018-01-28 ...
- 树莓派用U盘安装系统
* 需要使用Raspbian / Raspbian Lite或更高版本的2017-04-10版本 *不会SD卡装系统? 1. 先用装好Raspbian系统的SD卡启动 在命令行输入 echo ...
- JavaScript转unix时间戳
由于 unix 的时间戳是10位不带毫秒的,所以前端获取到时间戳之后需要做一下处理,才能获取正确的时间. // 假设这里是从服务端获取到的时间戳 var unixTime = data.time; / ...
- QQ浏览器等window.innerHeight首次读取的高度不正确的解决办法
问题描述 移动端的页面,需要处理首屏为一满屏.并且,采用javascript计算高度来设置容器高度的方案. <!DOCTYPE html> <html> <head> ...
- Erlang调度器细节探析
Erlang调度器细节探析 Erlang的很多基础特性使得它成为一个软实时的平台.其中包括垃圾回收机制,详细内容可以参见我的上一篇文章Erlang Garbage Collection Details ...
- FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. MetaException(message:javax.jdo.JDODataStoreException: An exception was thrown while adding/validating class(es) :
在hive命令行创建表时报错: FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. ...
- SAP进度条
一.代码示例: ),"行数 ll_tabix(),"循环标号 ll_pecnt DECIMALS ,"百分比 ll_pecet(),"百分数 ll_text( ...
- Go笔记-map
[概念] 1- map 是引用类型的 2- 声明方式 var map1 map[keytype]valuetype 例如:var map1 ma ...
- 在Android上编写模块化项目(翻译)
来源博客:Wang Jie's Blog 本文链接:http://blog.wangjiegulu.com/2018/02/13/writing_a_modular_project_on_androi ...