目的:编写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程序中配置:【例】
1 //数据库配置存放在配置文件db.properties中 2 3 //载入db.properties 4 Properties sqlConfig = new Properties(); 5 sqlConfig.load(new FileInputStream("db.properties")); //抛出IOException(FileNotFoundException) 6 //不抛出Exception的方法: 7 //sqlConfig.load(本类类名.getClassLoader().getResourceAsStream("db.properties")); 8 9 //读取db.properties中的数据 10 String driver = sqlConfig.getProperty("driver"); 11 String url = sqlConfig.getProperty("url"); 12 String username = sqlConfig.getProperty("username"); 13 String password = sqlConfig.getProperty("password"); 14 15 //配置数据源对象 16 DriverManagerDataSource ds = new DriverManagerDataSource(); 17 ds.setDriverClassName(driver); 18 ds.setUrl(url); 19 ds.setUsername(username); 20 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中数据的方法相同,此处不再赘述)
1 //配置数据源对象 2 BasicDataSource ds = new BasicDataSource(); 3 ds.setDriverClassName(driver); 4 ds.setUrl(url); 5 ds.setUsername(username); 6 ds.setPassword(password); 7 ds.setInitialSize(5); //池配置属性 8 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驱动
1 <dependencies> 2 <dependency> 3 <groupId>junit</groupId> 4 <artifactId>junit</artifactId> 5 <version>4.12</version> 6 <scope>test</scope> 7 </dependency> 8 <dependency> 9 <groupId>org.springframework</groupId> 10 <artifactId>spring-context</artifactId> 11 <version>${spring.version}</version> 12 </dependency> 13 <dependency> 14 <groupId>org.springframework</groupId> 15 <artifactId>spring-core</artifactId> 16 <version>${spring.version}</version> 17 </dependency> 18 <dependency> 19 <groupId>org.springframework</groupId> 20 <artifactId>spring-jdbc</artifactId> 21 <version>${spring.version}</version> 22 </dependency> 23 <dependency> 24 <groupId>org.springframework</groupId> 25 <artifactId>spring-tx</artifactId> 26 <version>${spring.version}</version> 27 </dependency> 28 <dependency> 29 <groupId>commons-dbcp</groupId> 30 <artifactId>commons-dbcp</artifactId> 31 <version>1.4</version> 32 </dependency> 33 <dependency> 34 <groupId>mysql</groupId> 35 <artifactId>mysql-connector-java</artifactId> 36 <version>${mysql.version}</version> 37 </dependency> 38 <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> 39 <dependency> 40 <groupId>org.mybatis</groupId> 41 <artifactId>mybatis</artifactId> 42 <version>3.4.4</version> 43 </dependency> 44 </dependencies>
1 driver=com.mysql.jdbc.Driver 2 url=jdbc:mysql://localhost:3306/students?useUnicode=true&characterEncoding=UTF-8&useSSL=false 3 username=root 4 password=pass 5 initialSize=3 6 maxActive=10
1 <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 2 <property name="ignoreUnresolvablePlaceholders" value="true"/> 3 <property name="location"> 4 <list> 5 <value>classpath:db.properties</value> 6 <value>classpath:jdbc.properties</value> 7 </list> 8 </property> 9 </bean>
applicationContext.xml:这里的头文件使用Spring Tool Suite生成的。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 6 xmlns:util="http://www.springframework.org/schema/util" 7 xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd 8 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd 10 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd"> 11 <!--<context:property-placeholder location="classpath:db.properties"/>--> 12 <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 13 <property name="location" value="classpath:db.properties"></property> 14 </bean> 15 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 16 <property name="username" value="${username}"></property> 17 <property name="password" value="${password}"></property> 18 <property name="url" value="${url}"></property> 19 <property name="driverClassName" value="${driver}"></property> 20 <property name="initialSize" value="${initialSize}"></property> 21 <property name="maxActive" value="${maxActive}"></property> 22 </bean> 23 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 24 <!--<bean id="jdbcTemplate" class="cn.cage.student.StudentDAOImpl">--> 25 <property name="dataSource" ref="dataSource"></property> 26 </bean> 27 </beans>
3.创建Student类,用于描述学生
修改:
* 1.添加hashCode和equals方法
* 2.将构造方法获取的参数由int改为Interger,否则在Mybatis查询中,会出现“找不到类型为[String,String,Integer]的构造函数”的报错。
1 /** 2 * @FileName:Student.java 3 * @description: 4 * @author Cage Yang 5 * @version 6 * Modified Date:2017年7月27日 7 * Why & What is modified: 8 * 1.添加hashCode和equals方法 9 * 2.将构造方法获取的参数由int改为Interger,否则在Mybatis查询中,会出现“找不到类型为[String,String,Integer]的构造函数”的报错。 10 */ 11 package cn.cage.student; 12 13 /** 14 * @ClassName Student 15 * @description 描述学生信息的类。 16 * @author Cage Yang 17 */ 18 public class Student implements Comparable<Student> { 19 private long id; 20 private long createTime; 21 private long updateTime; 22 private String name; 23 private String qq; 24 private String major; //学习方向 25 private String entryTime; 26 private String school; 27 private int jnshuId; //修真院ID 28 private String dailyUrl; 29 private String desire; 30 private String jnshuBro;//修真院师兄 31 private String knowFrom;//信息来源 32 /** 33 * @param name 学生姓名 34 * @param major 学习方向 35 * @param jnshuId 修真院ID 36 */ 37 public Student(String name, String major, Integer jnshuId) { 38 this.name = name; 39 this.major = major; 40 this.jnshuId = jnshuId; 41 } 42 /* (非 Javadoc) 43 * @see java.lang.Object#toString() 44 */ 45 @Override 46 public String toString() { 47 return "Student [name=" + name + ", major=" + major + ", jnshuId=" + jnshuId + "]"; 48 } 49 /* (非 Javadoc) 50 * @see java.lang.Object#hashCode() 51 */ 52 @Override 53 public int hashCode() { 54 final int prime = 31; 55 int result = 1; 56 result = prime * result + jnshuId; 57 result = prime * result + ((major == null) ? 0 : major.hashCode()); 58 return result; 59 } 60 /* (非 Javadoc) 61 * @see java.lang.Object#equals(java.lang.Object) 62 */ 63 @Override 64 public boolean equals(Object obj) { 65 if (this == obj) { 66 return true; 67 } 68 if (obj == null) { 69 return false; 70 } 71 if (!(obj instanceof Student)) { 72 return false; 73 } 74 Student other = (Student) obj; 75 if (jnshuId != other.jnshuId) { 76 return false; 77 } 78 if (major == null) { 79 if (other.major != null) { 80 return false; 81 } 82 } else if (!major.equals(other.major)) { 83 return false; 84 } 85 return true; 86 } 87 /* (非 Javadoc) 88 * @see java.lang.Comparable#compareTo(java.lang.Object) 89 */ 90 public int compareTo(Student o) { 91 // TODO 自动生成的方法存根 92 if (this.id!=o.id) { //其实相等就是指二者id都不存在(为0)的情况 93 return (new Long(this.id)).compareTo(new Long(o.id)); 94 } 95 if (!this.major.equals(o.major)) { 96 return this.major.compareTo(o.major); 97 } 98 return (new Integer(this.jnshuId).compareTo(new Integer(o.jnshuId))); 99 } 100 /** 101 * @return id 102 */ 103 public long getId() { 104 return id; 105 } 106 /** 107 * @param id 要设置的 id 108 */ 109 public void setId(long id) { 110 this.id = id; 111 } 112 /** 113 * @return name 114 */ 115 public String getName() { 116 return name; 117 } 118 /** 119 * @param name 要设置的 name 120 */ 121 public void setName(String name) { 122 this.name = name; 123 } 124 /** 125 * @return qq 126 */ 127 public String getQq() { 128 return qq; 129 } 130 /** 131 * @param qq 要设置的 qq 132 */ 133 public void setQq(String qq) { 134 this.qq = qq; 135 } 136 /** 137 * @return major 138 */ 139 public String getMajor() { 140 return major; 141 } 142 /** 143 * @param major 要设置的 major 144 */ 145 public void setMajor(String major) { 146 this.major = major; 147 } 148 /** 149 * @return entryTime 150 */ 151 public String getEntryTime() { 152 return entryTime; 153 } 154 /** 155 * @param entryTime 要设置的 entryTime 156 */ 157 public void setEntryTime(String entryTime) { 158 this.entryTime = entryTime; 159 } 160 /** 161 * @return school 162 */ 163 public String getSchool() { 164 return school; 165 } 166 /** 167 * @param school 要设置的 school 168 */ 169 public void setSchool(String school) { 170 this.school = school; 171 } 172 /** 173 * @return jnshuId 174 */ 175 public int getJnshuId() { 176 return jnshuId; 177 } 178 /** 179 * @param jnshuId 要设置的 jnshuId 180 */ 181 public void setJnshuId(int jnshuId) { 182 this.jnshuId = jnshuId; 183 } 184 /** 185 * @return dailyUrl 186 */ 187 public String getDailyUrl() { 188 return dailyUrl; 189 } 190 /** 191 * @param dailyUrl 要设置的 dailyUrl 192 */ 193 public void setDailyUrl(String dailyUrl) { 194 this.dailyUrl = dailyUrl; 195 } 196 /** 197 * @return desire 198 */ 199 public String getDesire() { 200 return desire; 201 } 202 /** 203 * @param desire 要设置的 desire 204 */ 205 public void setDesire(String desire) { 206 this.desire = desire; 207 } 208 /** 209 * @return jnshuBro 210 */ 211 public String getJnshuBro() { 212 return jnshuBro; 213 } 214 /** 215 * @param jnshuBro 要设置的 jnshuBro 216 */ 217 public void setJnshuBro(String jnshuBro) { 218 this.jnshuBro = jnshuBro; 219 } 220 /** 221 * @return knowFrom 222 */ 223 public String getKnowFrom() { 224 return knowFrom; 225 } 226 /** 227 * @param knowFrom 要设置的 knowFrom 228 */ 229 public void setKnowFrom(String knowFrom) { 230 this.knowFrom = knowFrom; 231 } 232 /** 233 * @return createTime 234 */ 235 public long getCreateTime() { 236 return createTime; 237 } 238 /** 239 * @return updateTime 240 */ 241 public long getUpdateTime() { 242 return updateTime; 243 } 244 /** 245 * @param createTime 要设置的 createTime 246 */ 247 public void setCreateTime(long createTime) { 248 this.createTime = createTime; 249 } 250 /** 251 * @param updateTime 要设置的 updateTime 252 */ 253 public void setUpdateTime(long updateTime) { 254 this.updateTime = updateTime; 255 } 256 257 258 }
4.创建数据传递接口,规定操作数据的方法
* 原因:不需要获取旧学生信息;由调用者提供id参数更好。
* 2.修改queryStuByName的返回值为List<Student>,因为可能出现的同名学生。
* 3.修改delStuByName的返回值为int,因为可能出现的同名学生。
* 4.删除delStuByName和delStuByJnshu两个方法。
* 原因:在实际业务逻辑中一般是先查询,然后根据查询结果选择删除。既然是这样,只需要根据选择项的id删除即可,不会出现误删的问题。
1 /** 2 * @FileName:DataOperate.java 3 * @description: 4 * @author Cage Yang 5 * @version 6 * Modified Date:2017年8月4日 7 * Why & What is modified: 8 * 1.将updateStu函数的返回值由Student修改为boolean,参数列表增加long id。 9 * 原因:不需要获取旧学生信息;由调用者提供id参数更好。 10 * 2.修改queryStuByName的返回值为List<Student>,因为可能出现的同名学生。 11 * 3.修改delStuByName的返回值为int,因为可能出现的同名学生。 12 * 4.删除delStuByName和delStuByJnshu两个方法。 13 * 原因:在实际业务逻辑中一般是先查询,然后根据查询结果选择删除。既然是这样,只需要根据选择项的id删除即可,不会出现误删的问题。 14 15 */ 16 package cn.cage.student; 17 18 import java.util.List; 19 20 /** 21 * @ClassName DataOperate 22 * @description 数据操作接口,规定了对学生对象数据的增删改查四种操作。 23 * @author Cage Yang 24 */ 25 public interface StudentDAO { 26 //增 27 public abstract boolean addStu(Student stu); 28 29 //删 30 public abstract boolean delStuById(long id); 31 // public abstract int delStuByName(String name); 32 // public abstract boolean delStuByJnshu(String major, int jnshuId); 33 public abstract boolean delStu(Student stu); 34 35 //改 36 public abstract boolean updateStu(Student stu, long id); 37 38 //查 39 public abstract Student queryStuById(long id); 40 public abstract List<Student> queryStuByName(String name); 41 public abstract Student queryStuByJnshu(String major, int jnshuId); 42 public abstract Student queryStu(Student stu); 43 }
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删除即可,不会出现误删的问题。
1 /** 2 * @FileName:StudentDAOSpringImpl.java 3 * @description: 4 * @author Cage Yang 5 * @version 6 * Modified Date:2017年8月4日 7 * Why & What is modified: 8 * 1.将updateStu函数的返回值由Student修改为boolean,参数列表增加long id。 9 * 函数体:将返回值设为“影响行数>0则返回true”;sql语句中的条件参数由stu.getId()改为id。 10 * 原因:不需要获取旧学生信息;由调用者提供id参数更好。 11 * 2.在addStu函数中,去掉sql语句中id的添加,id由数据库自增完成,以免发生id重复的情况。 12 * 3.将各查询方法中的RowMapper提取出来封装为一个类,在各查询方法中直接使用此类对象,避免重复代码。 13 * 为适配封装的QueryStuRowMapper,将各查询方法中的sql语句改为查询所有属性。 14 * 4.修改addStu函数体,当name、major、jnshuId为空时报错。 15 * 5.在updateStu函数体中也加上非空报错。 16 * 6.bug:各sql语句中的表名是student,数据库表名是students,应修改。 17 * 7.在通过id、专业+学号查询的方法中,通过try-catch捕获EmptyResultDataAccessException异常,给出查询结果为空的提示。 18 * 8.修改queryStuByName的返回值为List<Student>,因为可能出现的同名学生;然后通过query函数查询,且当结果size==0时,给出查询结果为空的提示。 19 * 9.修改delStuByName的返回值为int,因为可能出现的同名学生。 20 * 10.删除delStuByName和delStuByJnshu两个方法。 21 * 原因:在实际业务逻辑中一般是先查询,然后根据查询结果选择删除。既然是这样,只需要根据选择项的id删除即可,不会出现误删的问题。 22 */ 23 package cn.cage.student; 24 25 26 import java.util.List; 27 28 import org.springframework.context.ApplicationContext; 29 import org.springframework.context.support.ClassPathXmlApplicationContext; 30 import org.springframework.dao.EmptyResultDataAccessException; 31 import org.springframework.jdbc.core.JdbcTemplate; 32 33 /** 34 * @ClassName StudentDAOSpringImpl 35 * @description 36 * @author Cage Yang 37 */ 38 public class StudentDAOSpringImpl implements StudentDAO { 39 private ApplicationContext apc = null; 40 private JdbcTemplate jdbcTemplate = null; 41 42 /** 43 * 创建对象的同时获取jdbcTemplate对象。 44 */ 45 public StudentDAOSpringImpl() { 46 // TODO 自动生成的构造函数存根 47 apc = new ClassPathXmlApplicationContext("applicationContext.xml"); 48 jdbcTemplate = (JdbcTemplate) apc.getBean("jdbcTemplate"); 49 } 50 /* (非 Javadoc) 51 * @see cn.cage.student.StudentDAO#addStu(cn.cage.student.Student) 52 */ 53 public boolean addStu(Student stu) { 54 // TODO 自动生成的方法存根 55 String name = stu.getName(), major = stu.getMajor(); 56 int jnshuId = stu.getJnshuId(); 57 if (name==null || major==null || jnshuId==0) { 58 throw new RuntimeException("addStu:姓名、专业、学号不能为空!"); 59 } 60 String sql = "INSERT INTO students (name,qq,major,entrytime,gra_school,id_jnshu" 61 + ",daily_url,desire,bro_jnshu,knowfrom) VALUES (?,?,?,?,?,?,?,?,?,?)"; 62 int line = jdbcTemplate.update(sql,new Object[] { 63 name,stu.getQq(),major,stu.getEntryTime(), 64 stu.getSchool(),jnshuId,stu.getDailyUrl(),stu.getDesire(), 65 stu.getJnshuBro(),stu.getKnowFrom()}); 66 return line>0?true:false; 67 } 68 69 /* (非 Javadoc) 70 * @see cn.cage.student.StudentDAO#delStuById(long) 71 */ 72 public boolean delStuById(long id) { 73 // TODO 自动生成的方法存根 74 String sql = "DELETE FROM students WHERE id=?"; 75 int line = jdbcTemplate.update(sql,id); 76 return line>0?true:false; 77 } 78 79 /* (非 Javadoc) 80 * @see cn.cage.student.StudentDAO#delStuByName(java.lang.String) 81 */ 82 /* 83 public int delStuByName(String name) { 84 // TODO 自动生成的方法存根 85 String sql = "DELETE FROM students WHERE name=?"; 86 return jdbcTemplate.update(sql,name); 87 } 88 */ 89 /* (非 Javadoc) 90 * @see cn.cage.student.StudentDAO#delStuByJnshu(java.lang.String, int) 91 */ 92 /* 93 public boolean delStuByJnshu(String major, int jnshuId) { 94 // TODO 自动生成的方法存根 95 String sql = "DELETE FROM students WHERE id_jnshu=? and major=?"; 96 int line = jdbcTemplate.update(sql,new Object[] {jnshuId,major}); 97 return line>0?true:false; 98 } 99 */ 100 /* (非 Javadoc) 101 * 用于日后需要添加通过其他元素删除学生记录时的情况 102 * @see cn.cage.student.StudentDAO#delStu(cn.cage.student.Student) 103 */ 104 public boolean delStu(Student stu) { 105 // TODO 自动生成的方法存根 106 return false; 107 } 108 109 /* (非 Javadoc) 110 * @see cn.cage.student.StudentDAO#updateStu(cn.cage.student.Student) 111 */ 112 public boolean updateStu(Student stu, long id) { 113 // TODO 自动生成的方法存根 114 String name = stu.getName(), major = stu.getMajor(); 115 int jnshuId = stu.getJnshuId(); 116 if (name==null || major==null || jnshuId==0) { 117 throw new RuntimeException("updateStu:姓名、专业、学号不能为空!"); 118 } 119 String sql = "UPDATE students SET name=?,qq=?,major=?,entrytime=?,gra_school=?,id_jnshu=?" 120 + ",daily_url=?,desire=?,bro_jnshu=?,knowfrom=? WHERE id=?"; 121 int line = jdbcTemplate.update(sql,new Object[]{ 122 name,stu.getQq(),major,stu.getEntryTime(), 123 stu.getSchool(),jnshuId,stu.getDailyUrl(),stu.getDesire(), 124 stu.getJnshuBro(),stu.getKnowFrom(),id}); 125 return line>0?true:false; 126 } 127 128 /* (非 Javadoc) 129 * @see cn.cage.student.StudentDAO#queryStuById(int) 130 */ 131 public Student queryStuById(long id) { 132 // TODO 自动生成的方法存根 133 String sql = "SELECT id,create_at,update_at,name,qq,major,entrytime,gra_school" + 134 ",id_jnshu,daily_url,desire,bro_jnshu,knowfrom FROM students WHERE id=?"; 135 Student stu = null; 136 try { 137 stu = jdbcTemplate.queryForObject(sql, new QueryStuRowMapper(),id); 138 } catch (EmptyResultDataAccessException e) { 139 // TODO 此处可做更复杂的提示动作,比如抛出异常、记录到本地文件、显示到GUI等。 140 System.out.println("queryStuById:该学生不存在!"); 141 } 142 return stu; 143 } 144 145 /* (非 Javadoc) 146 * @see cn.cage.student.StudentDAO#queryStuByName(java.lang.String) 147 */ 148 public List<Student> queryStuByName(String name) { 149 // TODO 自动生成的方法存根 150 String sql = "SELECT id,create_at,update_at,name,qq,major,entrytime,gra_school" + 151 ",id_jnshu,daily_url,desire,bro_jnshu,knowfrom FROM students WHERE name=?"; 152 List<Student> list = jdbcTemplate.query(sql, new QueryStuRowMapper(),name); 153 if (list.size()==0) { 154 System.out.println("queryStuByName:该学生不存在!"); 155 } 156 return list; 157 } 158 159 /* (非 Javadoc) 160 * @see cn.cage.student.StudentDAO#queryStuByJnshu(java.lang.String, int) 161 */ 162 public Student queryStuByJnshu(String major, int jnshuId) { 163 // TODO 自动生成的方法存根 164 String sql = "SELECT id,create_at,update_at,name,qq,major,entrytime,gra_school,id_jnshu" + 165 ",daily_url,desire,bro_jnshu,knowfrom FROM students WHERE id_jnshu=? and major=?"; 166 Student stu = null; 167 try { 168 stu = jdbcTemplate.queryForObject(sql, new QueryStuRowMapper(),new Object[]{jnshuId,major}); 169 } catch (EmptyResultDataAccessException e) { 170 // TODO 此处可做更复杂的提示动作,比如抛出异常、记录到本地文件、显示到GUI等。 171 System.out.println("queryStuByJnshu:该学生不存在!"); 172 } 173 return stu; 174 } 175 176 /* (非 Javadoc) 177 * 用于日后需要添加通过其他元素查询学生记录时的情况 178 * @see cn.cage.student.StudentDAO#queryStu(cn.cage.student.Student) 179 */ 180 public Student queryStu(Student stu) { 181 // TODO 自动生成的方法存根 182 return null; 183 } 184 185 }
提取出的RowMapper实现类:
注:从MySQL中获取的Date值,在java中是java.sql.Date,toString的格式为YYYY-MM-dd。
修改:
* 1.给entrytime的设定语句加上非空判断,否则当其为空时,null.toString抛出空指针异常。
1 /** 2 * @FileName:QueryStuRowMapper.java 3 * @description: 4 * @author Cage Yang 5 * @version 6 * Modified Date:2017年8月4日 7 * Why & What is modified: 8 * 1.给entrytime的设定语句加上非空判断,否则当其为空时,null.toString抛出空指针异常。 9 */ 10 package cn.cage.student; 11 12 import java.sql.Date; 13 import java.sql.ResultSet; 14 import java.sql.SQLException; 15 16 import org.springframework.jdbc.core.RowMapper; 17 18 /** 19 * @ClassName QueryStuRowMapper 20 * @description 直接使用此类建立的对象时,sql语句应查询Student的所有属性。 21 * @author Cage Yang 22 */ 23 public class QueryStuRowMapper implements RowMapper<Student> { 24 /** 25 * 直接使用此类建立的对象时,sql语句应查询Student的所有属性。 26 * @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int) 27 */ 28 public Student mapRow(ResultSet rs, int rowNum) throws SQLException { 29 // TODO 自动生成的方法存根 30 Student stu = new Student(rs.getString("name"), rs.getString("major"), rs.getInt("id_jnshu")); 31 stu.setId(rs.getLong("id")); 32 stu.setCreateTime(rs.getLong("create_at")); 33 stu.setUpdateTime(rs.getLong("update_at")); 34 stu.setQq(rs.getString("qq")); 35 // TODO 从Mysql中获取的Date值,在java中是java.sql.Date,toString的格式为YYYY-MM-dd。 36 Date entryTime = rs.getDate("entrytime"); 37 if (entryTime!=null) { 38 stu.setEntryTime(entryTime.toString()); 39 } 40 stu.setSchool(rs.getString("gra_school")); 41 stu.setDailyUrl(rs.getString("daily_url")); 42 stu.setDesire(rs.getString("desire")); 43 stu.setJnshuBro(rs.getString("bro_jnshu")); 44 stu.setKnowFrom(rs.getString("knowfrom")); 45 return stu; 46 } 47 }
6.创建DAO工厂类,提供Impl对象
1 /** 2 * @FileName:StudentDAO.java 3 * @description: 4 * @author Cage Yang 5 * @version 6 * Modified Date:2017年7月27日 7 * Why & What is modified: <修改原因描述> 8 */ 9 package cn.cage.student; 10 11 /** 12 * @ClassName StudentDAO 13 * @description DAO工厂类,用于创建数据操作对象。 14 * @author Cage Yang 15 */ 16 public class DAOFactory { 17 public static StudentDAO getDAOImpl() { 18 return new StudentDAOSpringImpl(); 19 } 20 }
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方法。
1 /** 2 * @FileName:StuDaoImplTest.java 3 * @description: 4 * @author Cage Yang 5 * @version 6 * Modified Date:2017年8月8日 7 * Why & What is modified: <修改原因描述> 8 */ 9 package cn.cage.student; 10 11 import static org.junit.Assert.assertEquals; 12 import static org.junit.Assert.assertNull; 13 import static org.junit.Assert.assertTrue; 14 15 import java.util.Iterator; 16 import java.util.List; 17 18 import org.junit.AfterClass; 19 import org.junit.BeforeClass; 20 import org.junit.Test; 21 22 23 /** 24 * @ClassName StuDaoImplTest 25 * @description 26 * @author Cage Yang 27 */ 28 public class StuDaoImplTest { 29 private static StudentDAOSpringImpl stuDao = null; 30 /** 31 * @description 32 * @throws java.lang.Exception 33 */ 34 @BeforeClass 35 public static void setUpBeforeClass() throws Exception { 36 stuDao = new StudentDAOSpringImpl(); 37 } 38 39 @AfterClass 40 public static void tearDownAfterClass() throws Exception { 41 stuDao = null; 42 } 43 44 /** 45 * {@link cn.cage.student.StudentDAOSpringImpl#addStu(cn.cage.student.Student)} 的测试方法。 46 */ 47 @Test 48 public void testAddStu() { 49 Student stu = RandomStudent.getStudent(); 50 stu.setEntryTime("1995-8-6"); 51 assertTrue("插入失败!",stuDao.addStu(stu)); 52 assertEquals("插入错误,或查询byJnshu出错", stu, stuDao.queryStuByJnshu("Java后端工程师", 1501)); 53 } 54 55 /** 56 * {@link cn.cage.student.StudentDAOSpringImpl#delStuById(long)} 的测试方法。 57 */ 58 @Test 59 public void testDelStuById() { 60 assertTrue("删除失败!",stuDao.delStuById(3)); 61 assertNull("删除错误,或查询byId出错", stuDao.queryStuById(3)); 62 } 63 64 /** 65 * {@link cn.cage.student.StudentDAOSpringImpl#updateStu(cn.cage.student.Student, long)} 的测试方法。 66 */ 67 @Test 68 public void testUpdateStu() { 69 Student stu = RandomStudent.getStudent(); 70 stu.setDesire("哈哈哈哈哈哈哈哈"); 71 assertTrue("更新失败!", stuDao.updateStu(stu, 2)); 72 assertEquals("更新错误,或查询byId出错", "哈哈哈哈哈哈哈哈", stuDao.queryStuById(2).getDesire()); 73 } 74 75 /** 76 * {@link cn.cage.student.StudentDAOSpringImpl#queryStuByName(java.lang.String)} 的测试方法。 77 */ 78 @Test 79 public void testQueryStuByName() { 80 List<Student> list = stuDao.queryStuByName("王五"); 81 for (Iterator<Student> iterator = list.iterator(); iterator.hasNext();) { 82 Student student = (Student) iterator.next(); 83 if (student.getJnshuId()==1509) { 84 assertEquals("查询byName出错", "2017-08-06", student.getEntryTime()); 85 } 86 } 87 } 88 }
②测试结果
- 即使不存在安全性问题,MySQL也应该设定密码。否则可能出现程序无法连接到数据库的情况。
- POJO类中,尽量做到各属性名称、数据类型都和数据库中相同,这样可以免去很多麻烦。
- POJO类都要自定义toString、hashCode、equals、compareTo方法。特别是前三者,比较两个实例是否相同是一个很常见的操作,如果不自定义,根本无法比较。compareTo是考虑到实例可能存入TreeSet,最好自定义;不过即使不自定义,也可通过比较器来实现比较功能。
- 对于DAO接口及其实现类,增删改最好返回int型(影响行数);在insert、update数据时,要考虑到数据库设计时加了非空约束的列,对应属性为空值时直接抛出异常,不向数据库提交数据;delete方法一般只需要一个(比如id)即可,因为正常逻辑是先查询到要删除的数据,在根据查询结果中对应的id删除即可;在select数据时,要考虑到查询值不存在、查询值不唯一的情况,做出应对,避免抛出意料外的异常。
- 学习了JUnit4单元测试的编写与使用。