service和Dao,Mybatis---------18
service是业务层和dao是数据访问层:
记得以前刚学编程的时候,都是在service里直接调用dao,service里面就new一个dao类对象,调用,其他有意义的事没做,也不明白有这个有什么用,参加工作久了以后就会知道,业务才是工作中的重中之重。
我们都知道,标准主流现在的编程方式都是采用MVC综合设计模式,MVC本身不属于设计模式的一种,它描述的是一种结构,最终目的达到解耦,解耦说的意思是你更改某一层代码,不会影响我其他层代码,如果你会像spring这样的框架,你会了解面向接口编程,表示层调用控制层,控制层调用业务层,业务层调用数据访问层。初期也许都是new对象去调用下一层,比如你在业务层new一个DAO类的对象,调用DAO类方法访问数据库,这样写是不对的,因为在业务层中是不应该含有具体对象,最多只能有引用,如果有具体对象存在,就耦合了。当那个对象不存在,我还要修改业务的代码,这不符合逻辑。好比主板上内存坏了,我换内存,没必要连主板一起换。我不用知道内存是哪家生产,不用知道多大容量,只要是内存都可以插上这个接口使用。这就是MVC的意义。
接下来说你感觉service的意义,其实因为你现在做东西分层次不是那么严格,在一个你们做东西业务本身也少,举个最简单的例子,你做一个分页的功能,数据1000条,你20条在一个页,你可以把这个功能写成工具类封装起来,然后在业务层里调用这个封装的方法,这才是业务里真正干得事,只要没访问数据库的,都要在业务里写。
Mybatis
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
Ps1:配置文件的详细路径:src/test/java/org/apache/ibatis/submiited/complex_property/Configuration.xml
Ps2:小技巧:粘贴类名,再粘贴对象名,改大小写即可。
Ps3:顺序(从底层往高层):实体类-->Dao-->Service-->Servlet-->页面。
Ps4:数据库服务启动即可,无需登录数据库操作软件。
Ps5:Dao层一般与表(实体类)相关,命名一般在实体类名+“Dao”即可;Service层命名一般与Servlet命名对应。
Mybatis之sqlSession
/**
* 访问数据库类
*/
public class DBAccess {
public SqlSession getSqlSession() throws IOException {
// 通过配置文件获取数据库连接信息
Reader reader = Resources.getResourceAsReader("com/imooc/config/Configuration.xml");
// 通过配置信息构建一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 通过sqlSessionFactory打开一个数据库会话
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}=================================================================
Mybatis配置
Ps1:
<mapper namespace="">//namespace属性必须存在不可省略。
<select id="">
如果namespace名不同,则id可以一样,调用的时候,namespace名.id名;若namespace名相同,则id不能一样。
Ps2:
<mapper namespace="">
<resultMap id="">
//同上...
但是注意:resultMap——id可以与select——id一样(即使namespace相同),只要它们的id在各自领域(resultMap或select)范围内唯一即可。
Ps3:<resultMap>的子标签中,如果是主键栏,用<id>子标签,如果是普通栏,用<result>子标签。
Ps4:<select resultMap="resultMap中的id名">。
Ps5:<mappers>中子标签<mapper>的resource属性也是从src根目录开始算起,“.”改为“/”;如果有多个<mapper>可以继续写,个人理解相当于注册。
selectList();只能传一个参数,<select>标签中parameterType=""属性也只能接受一个参数的类型,所以常常封装传递的参数(如果是在lang包下,直接写类型名称,否则自定义写完整)。
<pre name="code" class="java">
<!-- mapper namespace="映射名" -->
<mapper namespace="Message">
<!-- 配置和类的连接: type="类全名" -->
<resultMap type="com.imooc.bean.Message" id="MessageResult">
<!-- column="数据库中的列名" jdbcType="数据类型" property="类中定义的名" -->
<id column="ID" jdbcType="INTEGER" property="id"/>
<result column="COMMAND" jdbcType="VARCHAR" property="command"/>
<result column="DESCRIPTION" jdbcType="VARCHAR" property="description"/>
<result column="CONTENT" jdbcType="VARCHAR" property="content"/>
</resultMap>
<!--select id="ID值(一个配置文件内不能重复)" parameterType="传入的参数(一般是类,只能是一个)" resultMap="MessageResult" -->
<select id="queryMessageList" parameterType="com.imooc.bean.Message" resultMap="MessageResult">
select <include refid="columns"/> from MESSAGE
<where>
<!--OGNL语言<if test="OGNL属性 != null and !""(""表示方法).equals(command.trim()(支持Java的所有方法))"> -->
<if test="command != null and !"".equals(command.trim())">
and COMMAND=#{command}
</if>
<if test="description != null and !"".equals(description.trim())">
and DESCRIPTION like '%' #{description} '%'
</if>
</where>
</select>
<sql id="columns">ID,COMMAND,DESCRIPTION,CONTENT</sql>
<delete id="deleteOne" parameterType="int">
delete from MESSAGE where ID = #{_parameter}
</delete>
<delete id="deleteBatch" parameterType="java.util.List">
delete from MESSAGE where ID in(
<foreach collection="list" item="item" separator=",">
#{item}
</foreach>
)
</delete>
</mapper>
/**
* 根据查询条件查询消息列表
*/
public List<Message> queryMessageList(String command,String description) {
DBAccess dbAccess = new DBAccess();//实例化数据库访问
List<Message> messageList = new ArrayList<Message>();
SqlSession sqlSession = null;
try {
sqlSession = dbAccess.getSqlSession();
Message message = new Message();
message.setCommand(command);
message.setDescription(description);
// 通过sqlSession执行SQL语句,传入映射ID,和对象(只能有一个对象)
messageList = sqlSession.selectList("Message.queryMessageList",message)
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return messageList;
}
=================================================================================================
OGNL可以直接调用Java类的方法;
OGNL中的特殊字符需要转义,如"转义为" &转义为&或者写为其特有操作符and。(遵循HTML转义规则)
MyBatis配置的时候,写SQL语句的时候,无需特意空格,MyBatis自动会识别加空格。
and COMMAND=?;(1,command)(注意:问号+问号赋值工程)等价于and COMMAND=#{command}。
关键代码:
<if test="command!=null and !"".equals(command.trim())">
and COMMAND=#{command}
</if>
<if test="description!=null && !"".equals(description.trim())">
and DESCRIPTION like '%' #{description} '%'
</if>
=======================================================================================================
============================================================
log4j.properties说明:
key=value
应用log4j(日志)调试动态SQL:
1、jar包和配置文件
2、log4j.properties
<1>log4j.rootLogger=DEBUG,Console:输出级别(级别<由低到高>debug/info/warn/error)和输出位置(控制台)
<2>log4j.appender.Console=org.apache.log4j.ConsoleAppender:配置这个类才会输出在控制台(可在别处)
<3>log4j.appender.layout=org.apache.log4j.PatternLayout:布局(按照自己的想法去输出)<4>log4j.appender.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n:
%d:产生日志的时间
[%t]:产生日志所处线程的线程名称
%-5p:输出日志的级别、
"5":代表输出的字符会占5位字符,不足则会用空格补齐;
"-":指的是补齐的空格在右边,没有则在左边.
[%c]:指输出这个日志时处于的那个类的全名,包括包名
%m:输出的时候附加的信息
%n:输出换行
<5>log4j.logger.org.apache=INFO:为不同包配不同的级别,把总的覆盖,可看到自己想看的信息
=============================================================
配置链接
<!-- mapper namespace="映射名" -->
<mapper namespace="Message">
<!-- 配置和类的连接: type="类全名" -->
<resultMap type="com.imooc.bean.Message" id="MessageResult">
<!-- column="数据库中的列名" jdbcType="数据类型" property="类中定义的名" -->
<id column="ID" jdbcType="INTEGER" property="id"/>
<result column="COMMAND" jdbcType="VARCHAR" property="command"/>
<result column="DESCRIPTION" jdbcType="VARCHAR" property="description"/>
<result column="CONTENT" jdbcType="VARCHAR" property="content"/>
</resultMap>
<!--select id="ID值(一个配置文件内不能重复)" parameterType="传入的参数(一般是对象)" resultMap="映射值(查询出来,要和resultMap中ID以一样)" -->
<select id="queryMessageList" parameterType="com.imooc.bean.Message" resultMap="MessageResult">
select <include refid="columns"/> from MESSAGE
<where>
<!--OGNL语言<if test="OGNL属性 != null and !""(""表示方法).equals(command.trim()(支持Java的所有方法))"> -->
<if test="command != null and !"".equals(command.trim())">
and COMMAND=#{command}
</if>
<if test="description != null and !"".equals(description.trim())">
and DESCRIPTION like '%' #{description} '%'
</if>
</where>
</select>
<sql id="columns">ID,COMMAND,DESCRIPTION,CONTENT</sql>
<delete id="deleteOne" parameterType="int">
delete from MESSAGE where ID = #{_parameter}
</delete>
<delete id="deleteBatch" parameterType="java.util.List">
delete from MESSAGE where ID in(
<foreach collection="list" item="item" separator=",">
#{item}
</foreach>
)
</delete>
</mapper>
@message
/**
* 和message表相关的数据库操作
*/
public class MessageDao {
/**
* 根据查询条件查询消息列表
*/
public List<Message> queryMessageList(String command,String description) {
DBAccess dbAccess = new DBAccess();//实例化数据库访问
List<Message> messageList = new ArrayList<Message>();
SqlSession sqlSession = null;
try {
sqlSession = dbAccess.getSqlSession();
Message message = new Message();
message.setCommand(command);
message.setDescription(description);
// 通过sqlSession执行SQL语句
messageList = sqlSession.selectList("Message.queryMessageList",message);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return messageList;
}
/**
* 单条删除
*/
public void deleteOne(int id) {
DBAccess dbAccess = new DBAccess();
SqlSession sqlSession = null;
try {
sqlSession = dbAccess.getSqlSession();
// 通过sqlSession执行SQL语句
sqlSession.delete("Message.deleteOne", id);
//手动提交
sqlSession.commit();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
}
/**
* 多条条删除
*/
public void deleteBatch(List<Integer> ids) {
DBAccess dbAccess = new DBAccess();
SqlSession sqlSession = null;
try {
sqlSession = dbAccess.getSqlSession();
// 通过sqlSession执行SQL语句
sqlSession.delete("Message.deleteBatch", ids);
sqlSession.commit();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
}
========================================================================
一对多的配置:
一个表提取类一个表(外键关联)的(List)集合,
父表:
<?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="Command">
<resultMap type="com.imooc.bean.Command" id="Command">
<id column="C_ID" jdbcType="INTEGER" property="id"/>
<result column="NAME" jdbcType="VARCHAR" property="name"/>
<result column="DESCRIPTION" jdbcType="VARCHAR" property="description"/>
<!-- <collection property="实体类属性名" resultMap="引用另一个文件(子表)"/> -->
<collection property="contentList" resultMap="CommandContent.Content"/>
</resultMap>
<select id="queryCommandList" parameterType="com.imooc.bean.Command" resultMap="Command">
select a.ID C_ID,a.NAME,a.DESCRIPTION,b.ID,b.CONTENT,b.COMMAND_ID
from COMMAND a left join COMMAND_CONTENT b
on a.ID=b.COMMAND_ID
<where>
<if test="name != null and !"".equals(name.trim())">
and a.NAME=#{name}
</if>
<if test="description != null and !"".equals(description.trim())">
and a.DESCRIPTION like '%' #{description} '%'
</if>
</where>
</select>
</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="CommandContent">
<resultMap type="com.imooc.bean.CommandContent" id="Content">
<id column="ID" jdbcType="INTEGER" property="id"/>
<result column="CONTENT" jdbcType="VARCHAR" property="content"/>
<result column="COMMAND_ID" jdbcType="VARCHAR" property="commandId"/>
</resultMap>
</mapper>
<update id="updateMessageList">
update MESSAGE
<set><!-- <set> 取代set标签-->
<if test="command != null and !"".equals(command.trim())">
COMMAND=#{command},
</if>
<if test="description != null and !"".equals(description.trim())">
DESCRIPTION =#{description} ,
</if>
<choose>
<when test="">
</when>
<when test="">
</when>
<otherwise>
</otherwise>
</choose>
</set>
</update>
<!-- prefix="where"//有内容田间where,||suff:后面添加text,||prefixOverrides:去除前面的and-->
<trim prefix="where" suffix=“test“ prefixOverrides="and" suffixOverrides=",">
</trim>
=============================================================================
mybatis常用的标签
其中select查询时不应该使用*号而是使用各列名,sql用来定义常量,多用于定义经常使用到的列名,然后通过include标签引用
==============================================================
一、resultMap和resultType:
当配置resultType时,就不需要配置resultMap,看似resultType方便,但是会被受限制,没有resultMap开放多。
相同点:都是表示查询结果集的类型。
不同点:
resultMap需要手动配置映射关系,而resultType是直接指定java类型或者自定义的实体类型,查询结果集的列名必须和实体属性名称一致(实体类:名称大小写可以忽略;java类型,如Map集合的key大小写要一致,尽量都大小写规范,如果不放心可以select ID id,...)。
优缺点:
1、resultType结果集列名要与java属性名一样,但是resultMap不受限制,因为resultMap有column来规定。
2、由于SQL类型与Java中类型部分不匹配,resultMap可以通过typeHandler=""来匹配(如:SQL中的0和1来表示java中的false和true;Date类型的转换),但是resultType无能为力。
二、parameterMap和patameterType:表示传入参数的对应关系,前者不推荐使用,只是mybatis为了适应以前的版本。
提示:看到Map字眼的想到映射关系,看到Type字眼的想到类型。
三、#{}和${}:相同点:都是用来作为占位符。
不同点:#{}在预编译的时候会呗替换为?,而${}在预编译的时候直接将变量的值替换进去,而且没有引号(所以还要加上“'${...}'”),故一般都是用前者,个别情况会使用后者:如需进行排序,且排序字段为参数时可以使用${}(order by后面不喜欢被预编译,所以使用${}更为恰当)。
四、#{}和ognl:
在#{}中如果是基本类型,其中的名称可以随便写(不推荐),但一般都用_parameter,因为值唯一,而ognl中必须写成_parameter的方式。
==============================================================
一、获取自增主键值:
<!-- //解析:添加数据(在主外键的关系中)可以使用 useGeneratedKeys="true",可获取自增长的id,并配合keyProperty="id"(java中实体类的属性名)指定该对象的主键值。 -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="com.imooc.bean.Command">
insert into command(name,description) values(#{name},#{description})
</insert>
二、找不到namespace.id的异常效果:1、在Configuration.xml没有配置<mappers><mapper resource="*.xml"></mapper></mappers>;
2、在1的配置中或selectList("Message.queryMessageList",message);中,名字写错。
三、排查SQL语法错误:
如果控制台出现SQL语句问题,复制到SQL软件上执行,若有参数,手动写上并执行。
四、不要过度使用${}。五、乱码问题:1. servlet传参时的编码:request.setCharacterEncoding("utf-8");或直接使用过滤器;
2. Java文件本身的编码;
3. 连接数据库的参数中,设定编码方式:@Configuration.xml
<property name="url" value="jdbc:mysql://127.0.0.1:3306/micro_message?useUnicode=ture&,characterEncoding=utf8"/>
4. 数据库、表的编码;
5. 展示页面的编码:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6. 浏览器编码问题等。