MyBatis--单表的CURD操作(原始dao方式)

MyBatis--单表的CURD操作(原始dao方式)

CURD操作,即指对数据库中实体对象的增create、改Update、查Read、删Delete操作

一、自定义Dao接口实现类

搭建环境

Dao接口:

public interface IStudentDao {
    //插入
    void insertStudent(Student student);
    void insertStudentCacheId(Student student);
    //删改
    void deleteStudentById(int id);
    void updateStudent(Student student);
    //查询所有
    List<Student> selectAllStudents();
    Map<String, Object> selectAllStudentsMap();
    //查询指定学生
    Student selectStudentById(int id);
    Student selectStudentByMap(Map<String, Object> map);
    //根据姓名查询
    List<Student> selectStudentsByName(String name);
}

Dao实现类:

public class StudentDaoImpl implements IStudentDao {

    private SqlSession sqlSession;

    @Override
    public void insertStudent(Student student) {
        
        try {
        
            sqlSession = MyBatisUtils.getSqlSession();
            sqlSession.insert("insertStudent",student);
            sqlSession.commit();
        } finally{
            if(sqlSession!=null){
                sqlSession.close();
            }
        }   
    }
}

测试:

public class MyTest {
    
    private IStudentDao dao;
    
    @Before
    public void before(){
        dao = new StudentDaoImpl();
    }
}

1.单纯插入数据

映射文件:

<insert id="insertStudent" parameterType="com.hcx.beans.Student">
        insert into 
        student(name,age,score) 
        values(#{name},#{age},#{score})
</insert>
  • id:该sql语句的唯一标识,java代码中要使用该标识。
  • "#{}":对指定参数类型属性值的引用。其底层是通过反射机制,调用student类相关属相的get方法来获取值得。因为底层使用的是反射,所以这里使用的是类的属性名,而非表的字段名。

dao实现类:

@Override
public void insertStudent(Student student) {
    
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        sqlSession.insert("insertStudent",student);
        sqlSession.commit();
    } finally{
        if(sqlSession!=null){
            sqlSession.close();
        }
    }   
}

注意:执行完对DB的修改操作,必须要做Sqlsession的提交。否则,修改将无法同步到DB中。因为使用无参的openSession()方法已经将事务自动提交功能给关闭了。

测试类:

@Test
public void test01(){       
    Student student = new Student("张三",23,99.8);
    dao.insertStudent(student);
}

2.插入后用新id初始化被插入对象

mysql中在插入语句后执行如下语句,则会输出新插入记录的id

insert into student(name,age,score) values('王五',25,95.5);
select @@identity;

映射文件中的<insert/>标签中,有一个子标签<selectKey/>用于获取新插入记录的主键值。

1.以下两种写法均可以完成“使用新插入记录的主键值初始化被插入的对象”的功能:

方式一:

<insert id="insertStudentCatchId" parameterType="com.hcx.beans.Student">
    insert into student(name,age,score) 
    values(#{name},#{age},#{score})
    <selectKey resultType="int" keyProperty="id" order="AFTER">
        select @@identity
    </selectKey>
</insert>

方式二:

<insert id="insertStudentCatchId2" parameterType="com.hcx.beans.Student">
    insert into student(name,age,score) 
    values(#{name},#{age},#{score})
    <selectKey resultType="int" keyProperty="id" order="AFTER">
        select last_insert_id();
    </selectKey>
</insert>
  • resultType:指出获取的主键的类型
  • keyProperty:指出主键在java类中对应的属性名。此处会将获取的主键值直接封装到被插入的student对象中,即dao中insert()方法的第二个参数对象中。
  • order:指出id的生成相对于insert语句的执行是在前还是在后。MySql数据库表中的id,均是先执行insert语句,而后生成id,所以需要设置为after;oracle数据库表中的id,则是在insert执行之前先生成,所以需要设置为before。当前的MyBatis版本,不指定order属性,则会根据所用DBMS,自动选择其值。

2.Dao实现类

@Override
public void insertStudentCacheId(Student student) {
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        sqlSession.insert("insesrtStudentCatchId",student);
        sqlSession.commit();
    }finally{
        if(sqlSession != null) {
            sqlSession.close();
        }               
    }
}

3.测试类:

//插入后用新id初始化被插入对象
@Test
public void test02(){
    Student student = new Student("张三",23,99.8);
    System.out.println("插入前student="+student);
    dao.insertStudentCatchId(student);
    System.out.println("插入后student="+student);
}

3.删除数据

1.映射文件:

<delete id="deleteStudentById">
    delete from student where id=#{id}
</delete>

注意:这里的动态参数id所赋值为#{id}。这个#{}表示这就是个占位符,代表delete()方法的第二个参数。#{}中可以放任意值,无需要与delete方法的第二个参数值相同。

2.dao实现类

@Override
public void deleteStudentById(int id) {
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        sqlSession.insert("deleteStudentById",id);
        sqlSession.commit();
    }finally{
        if(sqlSession != null) {
            sqlSession.close();
        }               
    }       
}

3.测试类

@Test
public void test03(){
    dao.deleteStudentById(3);       
}

4.修改数据

1.映射文件:

<update id="updateStudnet">
    update student 
    set name=#{name},age=#{age},score=#{score}
    where id=#{id}
</update>

注意,这里的#{}中,必须要填写update()方法所传第二个参数student对象的属性名称,不能随意填写。

2.dao实现类

@Override
public void updateStudent(Student student) {
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        sqlSession.update("updateStudnet",student);
        sqlSession.commit();
    }finally{
        if(sqlSession != null) {
            sqlSession.close();
        }               
    }       
}

3.测试类

@Test
public void test04(){
    Student student = new Student("得的",26,96.6);
    student.setId(5);
    dao.updateStudent(student);
    
}

5.查询所有对象-返回list

1.映射文件

<select id="selectAllStudents" resultType="com.hcx.beans.Student">
    select * from student
</select>

注意,resultType属性并非指查询结果最后的类型,而是每查出DB中的一条记录,将该记录封装成为的对象的类型。

2.dao实现类

完成dao实现类的selectAllStudent()方法。使用Sqlsession的selectList()方法完成查询操作。该方法会将查询出的每条记录封装为指定类型对象后,再将最后的结果集封装为list返回。

方法原型为:List selectList(String statement)
statement:映射文件中配置的SQL语句的id。

@Override
public List<Student> selectAllStudents() {
    List<Student> students = null;
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        students = sqlSession.selectList("selectAllStudents");
        //sqlSession.commit();
    }finally{
        if(sqlSession != null) {
            sqlSession.close();
        }               
    }
    return students;
}

注意:在写查询时,由于不是对DB中的数据进行修改,所以无需进行Sqlsession的提交。但最终Sqlsession对象还是需要关闭的。

3.测试类

@Test
public void test05(){
    List<Student> students = dao.selectAllStudents();
    for (Student student : students) {
        System.out.println(student);
    }       
}

6.查询所有对象-返回Map

1.映射文件

<select id="selectAllStudents" resultType="com.hcx.beans.Student">
    select * from student
</select>

2.dao实现类

完成dao实现类的selectStudentMap()方法。使用Sqlsession的selectMap()方法完成查询操作。该查询方法会将查询出的每条记录先封装为指定对象,然后再将该对象作为value,将该对象的指定属性所对应的字段名作为key封装为一个map对象。

方法原型:Map<Object,Object> selectMap(String statement,String mapKey)

  • statement:映射文件中配置的sql语句的id
  • mapkey:查询出的map所要使用的key。这个key为数据表的字段名。查询出的结果是一个map,每行记录将会对应一个Map.entry对象,该对象的key为指定字段的值,value为记录数据所封装的对象

代码:

@Override
public Map<String, Object> selectAllStudentsMap() {
    Map<String, Student> studentsMap = null;
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        studentsMap = sqlSession.selectMap("selectAllStudents", "name"); //name:指定所形成map的key
    } finally{
        if(sqlSession!=null){
            sqlSession.close();
        }
    }
    return null;
}

3.测试类

@Test
public void test06(){
    Map<String, Student> map = dao.selectAllStudentsMap();
    Student student = map.get("张三");
    System.out.println(student);
}

注意:若指定的作为key的属性值在DB中并不唯一,则后面的记录值会覆盖掉前面的值。即指定key的value值,一定是DB中该同名属性的最后一条记录值。

7.查询单个对象

1.映射文件

<select id="selectStudnetById" resultType="com.hcx.beans.Student">
    select * from student where id=#{id}
</select>

2.dao实现类

使用Sqlsession的selectOne()方法。其会将查询的结果记录封装为一个指定类型的对象。

方法原型:Object selectOne(String statement,Object parameter)

  • statement:映射文件中配置的SQL语句的id
  • parameter:查询条件中动态参数的值

代码:

@Override
public Student selectStudentById(int id) {
    Student student = null;
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        student = sqlSession.selectOne("selectStudentById",id);
        sqlSession.commit();
    }finally{
        if(sqlSession != null) {
            sqlSession.close();
        }               
    }
    return student;
}

3.测试类

@Test
public void test07(){
    Student student = dao.selectStudentById(3);
    System.out.println(student);
}

8.模糊查询

1.映射文件

写法一:

<select id="selectStudnetByName" resultType="com.hcx.beans.Student">
    select * from student 
    where name like concat('%',#{name},'%')
</select>

在进行模糊查询时,需要进行字符串的拼接。sql中的字符串的拼接使用的是函数concat(arg1,arg2,...)。注意不能使用java中的字符串连接符+。

写法二:

<select id="selectStudnetByName" resultType="com.hcx.beans.Student">
    select * from student 
    where name like '%' #{name} '%'
</select>

以上两种形式是等效的。都是以动态参数的形式出现在sql语句中的。

写法三:
还可使用如下方式,需要注意,只能使用${}中只能使用value,不能使用其他:

<select id="selectStudnetByName" resultType="com.hcx.beans.Student">
    select * from student 
    where name like '%${value}%'
</select>

这种方式是纯粹的字符串拼接,直接将参数拼接到了sql语句中,所以可能会发生sql注入

2.dao实现类

@Override
public List<Student> selectStudentsByName(String name) {
    List<Student> students = null;
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        students = sqlSession.selectOne("selectStudentByName",name);
        sqlSession.commit();
    }finally{
        if(sqlSession != null) {
            sqlSession.close();
        }               
    }
    return students;
}

3.测试类

@Test
public void test08(){
    List<Student> students = dao.selectStudentsByName("张");
    for (Student student : students) {
        System.out.println(student);
    }
}

$与#的区别:

#为占位符,$为字符串拼接符
字符串拼接是将参数值以硬编码的方式直接拼接到了sql语句中。
字符串拼接就会引发两个问题:
sql注入问题与没有使用预编译所导致的执行效率低下问题

应用场景:

一般情况下,动态参数的值是由用户输入的,则不能使用拼接符$,因为有可能会出现sql注入;若动态参数的值是由系统计算生成的,则可以使用拼接符$。但这样虽然不存在sql注入的风险,但仍存在执行效率问题。

9.根据map进行查询

mapper中sql语句的动态参数也可以是map的key

1.测试类

map中存放两个“键-值”对,即有两个key

@Test
public void test09(){
    Student student = new Student();
    student.setId(5);
    
    Map<String, Object> map = new HashMap<String,Object>();
    map.put("studentId", 2);
    map.put("student", student);
    
    student = dao.selectStudentByMap(map);
    System.out.println(student);
    
}

2.映射文件

<select id="selectStudentByMap" resultType="com.hcx.beans.Student">
    select * from student 
    where id=#{studentId}
</select>

<select id="selectStudentByMap2" resultType="com.hcx.beans.Student">
    select * from student 
    where id=#{student.id}
</select>

3.dao实现类

@Override
public Student selectStudentByMap(Map<String, Object> map) {
    Student student = null;
    try {
        sqlSession = MyBatisUtils.getSqlSession();
        student = sqlSession.selectOne("selectStudentByMap",map);
        sqlSession.commit();
    }finally{
        if(sqlSession != null) {
            sqlSession.close();
        }               
    }
    return student;
}

二、属性名与查询字段名不相同

resultType可以将查询结果直接映射为实体Bean对象的条件是,sql查询的字段名与实体Bean的属性名一致。因为在将查询结果转换为指定类型对象时,系统自动将查询结果字段名称作为对象的属性名,通过反射机制完成对象的创建。

当sql查询结果的字段名和实体Bean的属性名不一致时,将无法创建出需要类型的对象。此时有两种解决方案:查询字段使用别名或使用结果映射resultMap

1.修改student表

student表.PNG

2.dao接口

public interface IStudentDao {
    Student selectStudentById(int id);
}

3.测试类

public class MyTest {
    
    @Test
    public void test1(){
        IStudentDao dao = new StudentDaoImpl();
        Student student = dao.selectStudentById(3);
        System.out.println(student);
    }
}

4.dao实现类

public class StudentDaoImpl implements IStudentDao {

    @Override
    public Student selectStudentById(int id) {
        SqlSession sqlSession = null;
        Student student = null;
        try {
            sqlSession = MyBatisUtils.getSqlSession();
            student = sqlSession.selectOne("selectById2",id);
        } finally{
            if(sqlSession!=null){
                sqlSession.close();
            }
        }   
    }
}

解决方式一:查询字段使用别名

虽然属性名称与表中的字段名称不一样,但可以为查询结果的字段名称赋予别名,让别名与实体Bean的属性相同。这样框架也可以根据查询结果利用反射机制将对象创建。

在映射文件mapper中添加如下映射。注意,由于表的score字段名与student类的属性名相同,所以这里无需使用别名。

<select id="selectStudentById" resultType="Student">
    select tid id,tname name,tage,age,score
    from student where tid=#{tid}
</select>

解决方式二:使用结果映射resultMap

可以使用结果映射resultMap来建立映射关系,完成由字段到属性的映射,达到将查询结果封装为对象的目的。resultMap是对resultType的增强。

<resultMap type="Student" id="studentMapper">
    <id column="tid" property="id"/>
    <result column="tname" property="name"/>
    <result column="tage" property="age"/>
</resultMap>

<select id="selectStudentById" resultMap="studentMapper">
    select tid, tname, tage, score
    from student where tid=#{tid}
</select>

<resultMap/>标签中定义了由type指定的类的属性名到表中字段名称的映射关系。根据这个映射关系,框架利用反射机制创建相应对象。

  • type:指定要映射的实体类
  • id:指定该resultMap映射关系的名称
  • <id>标签:id的字段名column与实体类的属性property间的映射关系
  • <result>标签:id以外其他字段名column与实体类的属性property间的映射关系

如果字段名与实体类的属性名相同的情况,可以不写入<resultMap/>中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容