第一个例子就列举了简单插入的操作,接下来详细的描述每一个操作
1、搭建测试环境
有了之前的例子,这个是在之前的例子上面进行修改。
1.1 修改Dao接口IStudentDao
public interface IStudentDao {
void insertStu(Student student);
void insertStuCacheId(Student student);
void deleteStuById(int id);
void updateStu(Student student);
List<Student> selectAllStudent();
Map<String, Object> selectAllStudentMap();
Student selectStudentById(int id);
List<Student> selectStuByName(String name);
}
1.2 修改实现类StudentDaoImpl
public class StudentDaoImpl implements IStudentDao {
private SqlSession sqlSession;
public void insertStu(Student student) {
try {
sqlSession=MybatisUtil.getSqlSession();
sqlSession.insert("insertStudent",student);
sqlSession.commit();
}finally{
if(sqlSession!=null){
sqlSession.close();
}
}
}
public void insertStuCacheId(Student student) {
try {
sqlSession=MybatisUtil.getSqlSession();
sqlSession.insert("insertStuCacheId",student);
sqlSession.commit();
}finally{
if(sqlSession!=null){
sqlSession.close();
}
}
}
public void deleteStuById(int id) {
try {
sqlSession=MybatisUtil.getSqlSession();
System.out.println("要删除的id"+id);
//这里面的deleteStuById要和mapper里面的ID对应
sqlSession.delete("deleteStuById",id);
sqlSession.commit();
}finally{
if(sqlSession!=null){
sqlSession.close();
}
}
}
public void updateStu(Student student) {
try {
sqlSession=MybatisUtil.getSqlSession();
//4.增删改查的操作
sqlSession.update("updateStu",student);
sqlSession.commit();
}finally{
if(sqlSession!=null){
sqlSession.close();
}
}
}
public List<Student> selectAllStudent() {
List<Student> students = new ArrayList<Student>();
try {
sqlSession = MybatisUtil.getSqlSession();
students = sqlSession.selectList("selectAllStudents");
sqlSession.commit();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return students;
}
public Student selectStudentById(int id) {
Student student=null;
try {
sqlSession = MybatisUtil.getSqlSession();
student = sqlSession.selectOne("selectStudentById",id);
sqlSession.commit();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return student;
}
public Map<String, Object> selectAllStudentMap() {
Map<String,Object> map=new HashMap<String, Object>();
try {
sqlSession=MybatisUtil.getSqlSession();
map=sqlSession.selectMap("selectAllStudents", "name");
}finally{
if(sqlSession!=null){
sqlSession.close();
}
}
return map;
}
public List<Student> selectStuByName(String name) {
List<Student> students = null;
try {
sqlSession = MybatisUtil.getSqlSession();
students = sqlSession.selectList("selectStuByName",name);
sqlSession.commit();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return students;
}
}
这里需要注意的是,增删改的操作都需要对事务的提交,也就是执行sqlSession.commit()。但是查询操作不需要。
1.3 修改测试类MyTest
public class MyTest {
//由于增删改查都需要StudentDaoImpl,所以将其设置2全局变量。
private IStudentDao dao ;
@Before
public void testBefore(){
dao = new StudentDaoImpl();
}
@Test
public void testinsertStu(){
Student student = new Student("张三",23,93.5);
dao.insertStu(student);
}
@Test
public void testinsertStuCacheId(){
Student student = new Student("冯冬冬",20,97.5);
System.out.println("插入之前的id:"+student);
dao.insertStuCacheId(student);
System.out.println("插入之后的id:"+student.getId());
}
@Test
public void testDelete(){
dao.deleteStuById(30);
}
@Test
public void testUpdate(){
Student student = new Student("王胜男1",19,96.5);
student.setId(22);
dao.updateStu(student);
}
//选出数据库中所有的对象
@Test
public void testselectAllStudent() {
System.out.println("===========");
List<Student> students = dao.selectAllStudent();
for (Student student : students) {
System.out.println(student);
}
}
//根据id号选出学生
@Test
public void testselectStudentById() {
Student student = dao.selectStudentById(24);
System.out.println(student);
}
//根据名字选出学生
@Test
public void testselectStuByName() {
List<Student> students = dao.selectStuByName("冯");
for (Student student : students) {
System.out.println(student);
}
}
//根据map选出学生
@Test
public void testselectAllStudentMap() {
Map<String, Object> map=dao.selectAllStudentMap();
System.out.println(map.get("冯冬冬"));
}
}
1.4 修改mapper
<?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">
<mapper namespace="test">
<!-- parameterType已经定义了别名 -->
<insert id="insertStudent" >
insert into student(name,age,score) values(#{name},#{age},#{score})
</insert>
<insert id="insertStuCacheId" >
insert into student(name,age,score) values(#{name},#{age},#{score})
<!-- order:指定id的生成与插入语句的顺序关系 -->
<selectKey resultType="int" keyProperty="id" order="AFTER">
<!-- 这里有两种方式可以选择 -->
<!-- 1、select last_insert_id() -->
<!-- 2、select @@identity -->
select @@identity
</selectKey>
</insert>
<!-- id里面的内容是接口里面的方法名 ,提供给别人使用-->
<delete id="deleteStuById">
delete from student where id=#{xxx}
</delete>
<update id="updateStu">
update student set name=#{name},age=#{age},score=#{score} where id=#{id}
</update>
<select id="selectAllStudents" resultType="com.test.beans.Student">
select * from student
</select>
<select id="selectStudentById" resultType="com.test.beans.Student">
select * from student where id=#{id}
</select>
<select id="selectStuByName" resultType="com.test.beans.Student">
<!-- 这种查询方式表示只要含有xxx都会被查询到 -->
<!-- 下面这种方式推荐使用,可以方式sql注入。动态参数 -->
select * from student where name like '%' #{xxx} '%'
<!-- select * from student where name like concat('%',#{xxx},'%')-->
<!-- 下面这种情况表示纯粹的字符串拼接,在输出结果里面没有参数显示:这种情况不建议使用 -->
<!-- select * from student where name like '%${value}%'-->
</select>
</mapper>
2 增删改查操作的说明
2.1 插入:单纯插入数据
<insert id="insertStudent" >
insert into student(name,age,score) values(#{name},#{age},#{score})
</insert>
- id:该SQL语句的唯一标识,java代码中要使用这个标识,也就是在接口的实现类中:、
- #{}:对指定参数类型属性值的引用。其底层是通过反射机制,调用Student类相关属性的get方法来获取值得。因为底层使用的是反射,所以这里使用的是类的属性名,而不是表的字段名。也就是使用#{name}中的name对应Student学生类中的name属性,而不是DB数据库中的表的name字段。
2.2 插入:插入后用新id初始化被插入对象
<insert id="insertStuCacheId" >
insert into student(name,age,score) values(#{name},#{age},#{score})
<!-- order:指定id的生成与插入语句的顺序关系 -->
<selectKey resultType="int" keyProperty="id" order="AFTER">
<!-- 这里有两种方式可以选择 -->
<!-- 1、select last_insert_id() -->
<!-- 2、select @@identity -->
select @@identity
</selectKey>
</insert>
mysql中在插入语句后执行如下语句,则会输出新插入记录的id
或者是
映射文件的<insert/>标签,有一个字标签<selectKey/>获取新插入记录的主键值,上面的那两种写法都可以完成。
<selectKey resultType="int" keyProperty="id" order="AFTER">
<!-- 这里有两种方式可以选择 -->
<!-- 1、select last_insert_id() -->
<!-- 2、select @@identity -->
select @@identity
</selectKey>
- resultType:指出获取的主键的类型
- keyProperty:指出主键在java类(Student的id字段)对应的属性名。此处会将获取的主键值直接封装到被插入的Student对象中,也就是dao中insert()方法的第二个参数对象中。
- order:指出id的生成相对于insert语句的执行是在前还是在后。mysql数据表中的id都是先执行insert再生成id。所以需要设置AFTER。但是Oracle数据库表中的id,则是在insert方法之前生成,所以需要设置成BEFORE。当前的Mybatis版本,可以没有order,DBMS会自动选择其值。
此时我们可以修改测试类:
问题:id是何时获取到的?
测试结果2证明:插入操作还没有提交的时候已经有了id。说明了一个问题:无论插入操作是提交还是回滚,DB均会为insert的记录分配id,即使发生回滚,这个id也已经被使用。后面在插入时候,这个id就不能用了。也就是说,主键值得生成至于insert语句是否执行有关,而与最终是否提交无关。
测试:
2.3 删除:删除数据
<delete id="deleteStuById">
delete from student where id=#{xxx}
</delete>
注意:这里的#{xxx}并没有写id,而是任意值。为什么呢?因为此时这个#{xxx}就是一个占位符,代表sqlSession.delete方法的第二个操作。
接下来看实现:
看测试类:
@Test
public void testDelete(){
dao.deleteStuById(30);
}
2.4 修改:修改数据
<update id="updateStu">
update student set name=#{name},age=#{age},score=#{score} where id=#{id}
</update>
注意:这里的#{}中,必须要填写与update()方法所传入的第二个参数student对象的属性名称,不能随意填写。
看测试:
@Test
public void testUpdate(){
Student student = new Student("王胜男1",19,96.5);
student.setId(22);
dao.updateStu(student);
}
2.5 查询:
2.5.1 查询所有对象-返回list
<select id="selectAllStudents" resultType="com.test.beans.Student">
select * from student
</select>
注意:resultType属性并非指查询结果集最后的类型,而是查出的每一条记录的类型。这里的resultType使用的是全限定性类名。对于一个映射文件来说,一般情况下是对一个类的操作都放在同一个映射文件mapper.xml中。所以一个映射文件出现的类一般是相同的。而每一个需要指定类名的地方若都指定全限定类名会比较麻烦,因此推荐使用别名。
注册类的别名:有两种方式
再修改映射文件
<select id="selectAllStudents" resultType="Student">
select * from student
</select>
修改测试类:
//选出数据库中所有的对象
@Test
public void testselectAllStudent() {
System.out.println("===========");
List<Student> students = dao.selectAllStudent();
for (Student student : students) {
System.out.println(student);
}
}
2.5.2 查询所有对象-返回map
1、映射文件不需要修改
2、修改实现类
public Map<String, Object> selectAllStudentMap() {
Map<String,Object> map=new HashMap<String, Object>();
try {
sqlSession=MybatisUtil.getSqlSession();
map=sqlSession.selectMap("selectAllStudents", "name");
}finally{
if(sqlSession!=null){
sqlSession.close();
}
}
return map;
}
3、修改测试类
//根据map选出学生
@Test
public void testselectAllStudentMap() {
Map<String, Object> map=dao.selectAllStudentMap();
System.out.println(map.get("冯冬冬"));
}
说明:若指定的作为key的属性值在DB中并不唯一,则后面的记录值会覆盖前面的值。即指定key的value值,一定是DB中该同名属性的最后一条记录值。
2.5.3 根据id查询单个对象
<select id="selectStudentById" resultType="com.test.beans.Student">
select * from student where id=#{id}
</select>
接口的实现类如下:
测试类如下:
//根据id号选出学生
@Test
public void testselectStudentById() {
Student student = dao.selectStudentById(24);
System.out.println(student);
}
2.5.4 模糊查询
映射文件:
<select id="selectStuByName" resultType="com.test.beans.Student">
<!-- 这种查询方式表示只要含有xxx都会被查询到 -->
<!-- 下面这种方式推荐使用,可以方式sql注入。动态参数 -->
select * from student where name like '%' #{xxx} '%'
<!-- select * from student where name like concat('%',#{xxx},'%')-->
<!-- 下面这种情况表示纯粹的字符串拼接,在输出结果里面没有参数显示:这种情况不建议使用 -->
<!-- select * from student where name like '%${value}%'-->
</select>
第一种:
在进行模糊查询的时候,需要进行字符串的拼接。SQL中的字符串的拼接使用的是函数concat(args1,args2,...)。注意不能使用java的字符串拼接+。
第二种:
以上两种方式等效,都是动态参数的形式出现在SQL中
第三种:
注意:这种方式是纯粹的字符串拼接,直接将参数拼接到了SQL语句中。这种方式可能会发生SQL注入。
实现类:
测试类:
//根据名字选出学生
@Test
public void testselectStuByName() {
List<Student> students = dao.selectStuByName("冯");
for (Student student : students) {
System.out.println(student);
}
}
$与#的区别:
A:理论区别
$与#的区别是很大的,#表示占位符,$表示字符串拼接符
字符串拼接是将参数以硬编码的方式直接拼接到了SQL语句中。字符串拼接就会引发两个问题:sql注入与没有使用预编译所导致的执行效率低下问题。什么是sql注入和sql预编译呢?链接先放这,过两天整理
占位符的引用很好的解决了以上两个问题。
B:执行区别
先看占位符(1)
再看占位符(2)
再看拼接符
C:应用场景
一般情况下,动态参数的值是由用户输入的则不能使用拼接符$,因为有可能发生SQL注入,若动态参数的值是由系统计算生成的,则可以使用拼接符。但这样虽然不会发生SQL注入,但是带来了效率低下的问题。
OK,单表的操作就结束了,相比之下多表的操作比较麻烦,但是以上讨论的问题比较理想化,下一遍看当表的字段名字和类的属性名不一致怎么办的问题。