service层的作业+mybatis中的重要组件

一.JavaWeb中service层的作用

MVC = Jsp + Servlet + JavaBean
其中jsp View,servlet Controller,javaBean 是一个可复用的java对象。

BS项目
客户端发送请求 controller service dao
如果是CS项目
客户有一个界面【操作】 service dao
以上分析,BS 与 CS 的 service和dao都是一样的,这样分层,代码复用,而且容易多人协作开发。

DAO:最听话的,让你**增删改**你就增删改。没有情感,没有思想。
Controller:控制层,接收客户端请求的,客户端只能请求到servlet,不能请求到service,dao,其他
的java类。Controller的作用就是**接收客户端请求,调用其他java类**。

如果要删除一个账号,谁去组织代码的逻辑呢?
这一层就是业务逻辑层,简称Service 层。
举例理解Service层
ATM机器上,你插入银行卡要给 小红 转账,1 元
【界面】
	转账
		进入转账界面
			输入对方的卡号
			输入转账金额
		确认按钮
【controller】可以一键生成的
	获取对方卡号,获取转账金额
	获取当前登录用户(session会话跟踪)
【service】需要程序员根据业务需求自行编写,设计规则
	//jdbc事务
	public void transfer(String fromNo,String toNo,int money){
        //判断
        对方卡号存不存在
        对方卡的状态对不对
        你的余额够不够
        updateMoney(toNo,1);
        //停电了....
        updateMoney(fromNo,-1);
        给你发短信
        给对方发短信
        给你的微信公众号发消息
        添加转账记录.....
    }
【dao】可以一键生成的
	public void updateMoney(String cardNo,int money);

举例2

注册账号:
【界面】
	填写你的账号
	输入两次密码
	手机验证码可选的(越来越多,短信不能随便发了)
【controller】 servlet(springMVC)
    try{
        接收数据 String
        数据封装,数据类型转换,封装为实体对象
        调用--》service
        显示结果-->请求转发/重定向到页面 或者 输出的是json
    }catch(Exception e){
        //对异常进行显示即可
        String msg = e.getMessage();
        request.setAttribute("msg",msg);
        //请求转发到注册jsp,jsp ${msg}
        //如果前端是ajax
        //response.getWrite().print("{result:false,msg:“+msg+”}");
    }
【service】业务逻辑
public void register(Account account){
    //判断,非空判断,长度判断,密码安全性判断【前端不是有js判断,前端判断对程序员无效】
    throw new RuntimeException("密码长度不足6位");
    //两次密码一致性判断
    throw new RuntimeException("两次密码不一致");
    //用户名or手机号是否被注册判断....
    //密码进行加密储存--> MD5【前端传输数据的时候就已经进行MD5的加密了】
    //调用dao保存数据
    //日志,xxx 什么 时间 xxx 注册了,什么 ip
    //发送短信
}
【dao】
    public Account queryByName(String name){}
    public void save(Account account){}

二.MyBatis重要组件

小结mybatis开发步骤:

增加mybatis的依赖,mysql jar 的依赖

编写mybatis-config.xml 核心配置文件【是mybatis运行的基础,mybatis所有的组件都是基于配置文
件运行的】

编写model,实体类,编写mapper.xml映射文件【就是编写sql代码,达到sql和java代码分离以便于容
易的维护sql。以及提高sql的复用】

使用mybatis提供的API,对数据库进行操作【接口,类很多】

1.Resources类

将文件读取为文件输入流。

注意事项:

// "mybatis-config.xml" 从当前类所在的包查找文件
// "/mybatis-config.xml" 从classes根目录下寻找

Resources类
将文件读取为 文件输入流
// "mybatis-config.xml" 从当前类所在的包查找文件
// "/mybatis-config.xml" 从classes根目录下寻找
InputStream resourceAsStream1 =
AccountMapperTest.class.getClass().getResourceAsStream("mybatis-config.xml");
System.out.println(resourceAsStream1);
//从classes根目录查找文件
//建议使用
InputStream resourceAsStream2 =
AccountMapperTest.class.getClassLoader().getResourceAsStream("mybatisconfig.xml");
System.out.println(resourceAsStream2);
//读取核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatisconfig.xml");

反射方式读取文件

(1)、AppClass.class.getClassLoader().getResourceAsStream("file")

从类路径classpath,也就是classes根目录下读取文件。[推荐]

//1、读取核心配置文件
//InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
InputStream in = AccountMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//2、创建SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

(2)、AppClass.class.getResourceAsStream(file)

// "mybatis-config.xml" 从当前类所在的包查找文件
// "/mybatis-config.xml" 从classes根目录下寻找

//1、读取核心配置文件
//InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//InputStream in = AccountMapperTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
InputStream in = AccountMapperTest.class.getResourceAsStream("/mybatis-config.xml");
//2、创建SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

上述三种方式都可,在MyBatis中推荐使用Resources类。

2.api的生命周期

注意api的生命周期

(1) SqlSessionFactoryBuilder

SqlSessionFactoryBuilder
构建sqlsessionfactory
一旦构建了sqlsessionfactory builder的作用就达到了,
builder生命周期 就是方法范围

(2) SqlSessionFactory

SqlSessionFactory
接口
这是一个重量级的对象,他的作用是获取sqlsession,是一本工厂设计模式,应该被设计为系统唯一。
如何去实现呢?
单例模式
静态块初始化【简单】
public interface SqlSessionFactory {
     
	SqlSession openSession();//使用最多的
	SqlSession openSession(boolean autoCommit);
	SqlSession openSession(Connection connection);
	SqlSession openSession(TransactionIsolationLevel level);
	SqlSession openSession(ExecutorType execType);
	SqlSession openSession(ExecutorType execType, boolean autoCommit);
	SqlSession openSession(ExecutorType execType, TransactionIsolationLevel
		level);
	SqlSession openSession(ExecutorType execType, Connection connection);
	Configuration getConfiguration();
}

(3) SqlSession

SqlSession
sql session
和数据库的一次会话,本质 connection+statement 。。PS
生命周期应该是方法内的,不能放在全局,类的属性都不行,而且特别还要注意用完即关闭,必须要关
闭,否则连接资源耗尽


每个线程都应该有它自己的 SqlSession 实例。

SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。

如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
     
  // 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

3.代码的改进

针对上面的点,对上次的代码进行改进

通过对mybaits提供的编程接口声明周期的理解,可以更好的组织程序代码。

package com.oracle.util;


import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;


/**
 * 编写mybatis的工具类,工具类最核心的就是为程序中各个mapper
 * 提供 sqlsession ,因为mapper要和 xml 打交道,打交道进行crud操作
 * 都是使用的sqlsession 提供的方法
 * 

* 如何保证sqlsessionfactory是系统唯一呢? * 所谓单例 整个应用中,仅有一个类的实例存在 */ public class MybatisUtil { //私有化构造 private MybatisUtil() { } //避免线程的不可见行 private volatile static SqlSessionFactory sqlSessionFactory; public static SqlSessionFactory getSqlSessionFactory() { //双重锁 if (sqlSessionFactory == null) { synchronized (MybatisUtil.class) { if (sqlSessionFactory == null) { try { //在这里初始化即可 InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); } catch (Exception e) { System.err.println("初始化sqlsessionfactory异常:" + e.getMessage()); System.exit(0); } } } } return sqlSessionFactory; } }

现阶段mapper【以前的dao】代码编写

package com.oracle.mapper;

import com.oracle.model.Account;
import com.oracle.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;


import java.util.List;

public class AccountMapper {
     
    public List<Account> selectAll(){
     
        SqlSession sqlSession = MybatisUtil.getSqlSessionFactory().openSession();
        try {
     
            //查询多个用selectList,查询一个用selectOne
            return sqlSession.selectList("com.oracle.mapper.AccountMapper.selectAll");
        }finally {
     
            sqlSession.close();
        }
    }

    public Account selectById(Integer aid){
     
        SqlSession sqlSession = MybatisUtil.getSqlSessionFactory().openSession();
        try {
     
            return sqlSession.selectOne("com.oracle.mapper.AccountMapper.selectById",aid);
        }finally {
     
            sqlSession.close();
        }
    }

    public void insert(Account account){
     
        SqlSession sqlSession = MybatisUtil.getSqlSessionFactory().openSession();
        try {
     
            sqlSession.insert("com.oracle.mapper.AccountMapper.insert",account);
        }finally {
     
            sqlSession.close();
            sqlSession.commit();
        }
    }

    public void update(Account account) {
     
        SqlSession sqlSession = MybatisUtil.getSqlSessionFactory().openSession();
        try {
     
            sqlSession.update("com.oracle.mapper.AccountMapper.update",account);
            sqlSession.commit();
        }finally {
     
            sqlSession.close();
        }
    }

    public void delete(Integer aid) {
     
        SqlSession sqlSession = MybatisUtil.getSqlSessionFactory().openSession();
        try {
     
            sqlSession.update("com.oracle.mapper.AccountMapper.delete",aid);
            sqlSession.commit();
        }finally {
     
            sqlSession.close();
        }

    }

}

AccountMapper.xml









<mapper namespace="com.oracle.mapper.AccountMapper">
    
    
    
    
    
    <select id="selectAll" resultType="com.oracle.model.Account">

        select aid,aname,apassword,a_nickname as anikename from account
    select>

    
    <select id="selectById" resultType="com.oracle.model.Account">
        select aid,aname,apassword,a_nickname as anikename from account where aid=#{aid}
    select>

    
    
   <insert id="insert">
       insert
       into
       account(aname,apassword,a_nickname)
       values(#{aname},#{apassword},#{anikename})
   insert>

    <update id="update">
        update account
        set aname = #{aname}, apassword = #{apassword}, a_nickname = #{anickname}
        where aid = #{aid}
    update>

    <delete id="delete">
        delete from account
        where aid = #{aid}
    delete>


mapper>

AccountMapperTest.java

package com.oracle.mapper;

import com.oracle.model.Account;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

public class AccountMapperTest4 {
     

    private AccountMapper accountMapper;

    @Before
    public void before() {
     
        accountMapper = new AccountMapper();
    }

    @Test
    public void selectAll(){
     
        List<Account> list = accountMapper.selectAll();
        System.out.println(list);
        /*
        *
        * [Account{aid=1, aname='zhangsan', apassword='123456', anikename='张三'}, Account{aid=2, aname='lisi', apassword='123456', anikename='李四'},
        * Account{aid=4, aname='wangwu', apassword='123456', anikename='王五'}]
        * */
    }

    @Test
    public void selectById(){
     
        Account account = accountMapper.selectById(1);
        System.out.println(account);
        //Account{aid=1, aname='zhangsan', apassword='123456', anikename='张三'}
    }

    @Test
    public void update(){
     
        Account account = new Account();
        account.setAid(5);
        account.setAname("wuhu");
        account.setApassword("123456");
        account.setAnickname("wuhuwuhu");
        accountMapper.update(account);
    }
    @Test
    public void insert(){
     
        Account account = new Account();
        account.setAname("aaa");
        account.setApassword("aaa");
        account.setAnickname("aaa");
        accountMapper.insert(account);
    }
    @Test
    public void delete(){
     

        accountMapper.delete(4);
    }
}

三.优化开发(日志别名配置文件)

mybatis-config.xml

1.配置properties

将数据库访问参数放在resources资源目录下的db.properties文件。

# 配置文件的key前面最好加上文件名
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql:///mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
db.username=root
db.password=123456

在MyBatis核心配置文件中通过properties标签引入。mybatis-config.xml




<properties resource="db.properties"> properties>

获取

<!--环境-->
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${db.driver}"/>
            <property name="url" value="${db.url}"/>
            <property name="username" value="${db.username}"/>
            <property name="password" value="${db.password}"/>
        </dataSource>
    </environment>
</environments>

核心配置文件:

<?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">

<!--根节点configuration 配置-->
<configuration>

    <!--加载并读取配置文件-->
    <!--resource 从classes目录下寻找配置文件-->
    <!--属性文件中的值 使用 ${
     key}-->
    <properties resource="db.properties"> </properties>

    <!--环境,很多环境,一个程序可能操作多个数据源(数据库),而且我们开发环境和本地测试环境也不一样-->
    <!--对于不同数据库的操作我们理解为环境-->
    <!--default就是程序默认使用的数据源-->
    <!--环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>


    <!--加密mapper的映射文件-->
    <mappers>
        <!--mapper文件的全路径,注意使用/分隔-->
        <mapper resource="com/oracle/mapper/AccountMapper.xml"></mapper>
    </mappers>
    

</configuration>

2.别名

别名,在mapper.xml文件中,有一个xml的属性 resultType,它表示select标签执行后的java数据类
型。

<select id="selectAll" resultType="com.oracle.model.Account">
select
aid,aname,apass,a_nikename as anikename
from
account
select>

所谓别名映射,就是指 com.oracle.model.Account 类的全称可以简写


<typeAliases>






<package name="com.oracle.model"/>
typeAliases>

<typeAliases>
    
    
    
    

    
    
    <package name="com.oracle.model"/>
    
typeAliases>

3.映射文件mapper

在mybatis-config.xml中加载了mapper.xml。

如果mapper.xml很多,引入不方便,后期会采用Mapper代理。

 
    <mappers>
        
        <mapper resource="com/oracle/mapper/AccountMapper.xml"/>
        
        
    mappers>

4. #{}和${}区分

#{}和${}都表示参数的替换,不同之处在于:

#{},占位符,会根据参数的类型,自动选择是否增加单引号,本质就是利用了PreparedStatement;

${}只是替换,也就是说,你传递String字符串,也是直接替换,不会为你自动增加单引号;

${ }只是替换,也就是说,你传递 String类型,也直接替换
#{ }使用的多 ; ${}模糊查询,最大的缺点sql注入【 ' or 1=1 -- 】

a. #{ }

#{ }使用的是OGNL 表达式获取对象中的数据

<insert id="insert" >
    insert
    into
    account(aname,apassword,a_nikename)
    values
    (#{
     aname},#{
     apassword},#{
     anikename})
</insert>

public void insert(Account account) {
     
    try (SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession()) {
     
        sqlSession.insert("com.oracle.mapper.AccountMapper.insert", account);
        sqlSession.commit();
    }
}

以上两个代码结合,就是传递了Account对象,但是在sql运行的时候,获取到了对象的属性值,这种从
对象中获取属性值的方式,是通过OGNL对象图表达式语言完成的。

总结:#{}如果参数是非自定义对象,值可以随意填写;如果参数是自定义对象,那么值必须为属性。

#{}可以根据数据类型自动选择是否增加单引号。

5.${}

(1)

当参数是Integer时,#{aid}我们发现是能够获取值的



但是,${aid}会报错,


@Test
    public void selectById(){
        Account account = accountMapper.selectById(1);
        System.out.println(account);
        //Account{aid=1, aname='zhangsan', apassword='123456', anikename='张三'}
    }

错误:

### Error querying database.  Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'aid' in 'class java.lang.Integer'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'aid' in 'class java.lang.Integer'

${ }对于int,Integer 数值类型,要求必须传递 v a l u e , 一 旦 识 别 {value} ,一旦识别 value,{value}就直接替换,不使用OGNL

如果参数是对象类型,${ 对象的属性 },还是通过OGNL运算给数值

这里是把Integer当做了自定义对象,寻找属性aid了,然后就会报找不到错误。

a i d 改 为 {aid}改为 aid{value}。

 

与之类似的还有String类型参数。

如果参数是String类型,${aname}的情况下,把String当做了自定义对象,寻找aname属性;

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'aname' in 'class java.lang.String'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'aname' in 'class java.lang.String'

${ }只是替换

org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Unknown
column '李四' in 'field list'
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: insert into account(aname,apass,a_nikename)
values (李四,33,张三)
### Cause: java.sql.SQLSyntaxErrorException: Unknown column '李四' in 'field
list'

6.#{}

模糊查找使用#{}

如果参数是对象类型,${对象的属性},还是通过OGNL运算给数值。

但是,${对象的属性}只是替换,不会因为你是字符串就自动给你加上单引号。

ps:所以在传递对象作为参数时,选用#{}是最好的选择。

AccountMapper.xml

 <select id="selectByName" resultType="account">
        select aid, aname, apassword, a_nickname as anickname
        from account
        where aname like #{aname}
    select>

AccountMapper.java

public List<Account> selectByName(String aname){
     
        SqlSession sqlSession = MybatisUtil.getSqlSessionFactory().openSession();
        try {
     
            return sqlSession.selectList("com.oracle.mapper.AccountMapper.selectByName",aname);

        }finally {
     
            sqlSession.commit();
            sqlSession.close();
        }
    }

测试方法

  @Test
    public void selectByName(){
     
        List<Account> list = accountMapper.selectByName("%zhang%");
        System.out.println(list);
    }

如果使用#{},那么在参数中必须把%设置好了。

[Account{aid=1, aname='zhangsan', apassword='123456', anickname='张三'}]

使用${}带来的问题



AccountMapper.java

//模糊查询
public List<Account> selectByName(String name) {
     
    try (SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactory().openSession()) {
     
        return sqlSession.selectList("com.oracle.mapper.AccountMapper.selectByName2", name);
    }
}

测试:

@Test
public void selectByName() {
     
    List<Account> list = accountMapper.selectByName("zhang");
    System.out.println(list);
}

结果:

[Account(aid=1, aname=zhangsan, apassword=nasgnahz, anickname=张三)]

但是,${}存在SQL注入的风险,慎用。

@Test
public void selectByName() {
     
    List<Account> list = accountMapper.selectByName("a' or 1 = 1 -- ");
    System.out.println(list);
}

最终的SQL为:

SELECT * FROM account WHERE aname LIKE '%a' OR 1 = 1 -- %';

1后面的--为SQL语言的注释。

查询结果为数据库中的所有数据:

[Account(aid=1, aname=zhangsan, apassword=nasgnahz, anickname=张三), Account(aid=2, aname=lisi, apassword=isil, anickname=李四)]

总结:${}如果参数是非自定义对象,那么值只能为value;如果参数是自定义对象,那么值必须为属性;

${}存在Sql注入的风险;

${}不会根据数据类型自动增加单引号。

7.得到插入数据的主键值

得到插入数据的主键值
比如说qq号,注册qq号,可能qq号是数据库的主键值。
在比如插入类别和商品,注意一次性插入,插入类别后返回主键值





    
    
    
    
    insert
    into
    account(aname,apass,a_nikename)
    values
    (#{aname},#{apass},#{anikename})
    

MyBatis支持插入数据并返回数据库中的主键值。

数据库的id值存在两种情况:

  • 数据库自动维护id的值

    • sqlserver
    • mysql
  • 另一种是序列sequence

    • oracle

      
      
      
      
          insert into account(aname, apassword, a_nickname)
          values
          (#{aname}, #{apassword}, #{anickname})
      
      
      
@Test
public void insert() {
     
    Account account = new Account();
    account.setAname("wangwu");
    account.setApassword("uwgnaw");
    account.setAnickname("王五");
    accountMapper.insert(account);
    System.out.println(account.getAid());
    //3
}

last_insert_id()函数





    
        select last_insert_id()
    
    insert into account(aname, apassword, a_nickname)
    values
    (#{aname}, #{apassword}, #{anickname})


@Test
public void insert() {
     
    //2、插入一条记录
    Account account = new Account();
    account.setAname("john");
    account.setApassword("123456");
    account.setAnickname("jj");
    accountMapper.insert(account);
    System.out.println(account.getAid());
    //4
}

你可能感兴趣的:(Mybatis,数据库,mybatis,mysql,java,spring)