Maven
一、Maven基本概念
在之前的学习中我们知道如果要开发一个项目,那么肯定会使用到一堆的*.jar包,之前我们并没有发现有任何问题,那是因为我们目前使用 *.jar少,如果在以后的中大型项目中至少出现上百个甚至上千个这样的 *.jar是很正常的。此时如果在手工的去下载,去管理就是一场噩梦。为了解决这一问题,Maven出现了,只需要在配置文件中进行配置就行了。
Maven可以实现项目的打包、编译、对jar包的管理,其中最重要功能是对jar包的管理。maven项目一定有一个pom.xml,我们在相关的pom.xml文件中进行简单的配置之后,就会自动下载我们所需要的jar包。
Maven有很多优点,但是也有很多缺点需要你去忍受,比如说创建了项目之后会在项目上出现错误,但是这个错误不会影响你的开发,此时不要去折腾他。要求你的网络要好,如果网络不好,下到一半的时候出现问题,要删除之前下载的,重新来,甚至要重建项目。
二、Maven的配置
- 下载Maven开发包,maven是一个工具,既然是工具就需要下载安装(或者解压即可)(http://maven.apache.org/)
- 将Maven的开发包解压到d盘,解压之后里面会有一个配置文件(settings.xml),可以在该文件中进行相关配置以提高下载jar文件的速度,否则下载的时候是在国外空间进行下载是非常慢的
- 创建一个Maven项目
- 配置组织名称和项目名称
GroupId(俗称:包结构)、ArtifactId(俗称:项目名)。
GroupID是项目组织唯一的标识符,一般包含多段,第一段为域,第二段为公司名称,实际对应项目的包名称;而ArtifactID就是项目的唯一的标识符,实际对应项目名称。
- 创建成功
src/main/java:该目录是保存java源代码
src/main/resource:保存后面的一些配置文件
src/test:一些测试程序可以在这里去定义
pom.xml:这是依赖(jar文件)管理的配置文件
- 配置idea的maven环境(如果你不配置则表示默认处理)
- 使用maven下载我们需要的jar文件(数据库连接驱动和json处理的jar)
- 调整apache-maven/conf/settings.xml文件,目的地修改下载的仓库为国内的
<mirrors>
<!-- mirror | Specifies a repository mirror site to use instead of a given
repository. The repository that | this mirror serves has an ID that matches
the mirrorOf element of this mirror. IDs are used | for inheritance and direct
lookup purposes, and must be unique across the set of mirrors. | -->
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
<mirror>
<id>net-cn</id>
<mirrorOf>central</mirrorOf>
<name>Nexus net</name>
<url>http://maven.net.cn/content/groups/public/</url>
</mirror>
</mirrors>
配置pom.xml文件仓库地址:
https://www.mvnrepository.com/
cn.bing.com
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qf</groupId>
<artifactId>mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!--数据库连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.43</version>
</dependency>
<!--mybatis的开发包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--日志打印的依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
- 对maven项目重新导入
此时使用的是默认的settings.xml文件,我们需要配置指定的settings.xml文件在该文件中我们指定下载相关插件或者*.jar的地址,指定为国内仓库。一般是使用阿里云的是比较稳定的(稍后去修改settings.xml文件)
Mybatis
⼀、Mybatis的基本概念
分析之前是怎么去操作数据库,⾸先要取得Connection接⼝对象,需要使⽤DriverManager类取得,之后还需要使⽤PreparedStatement接⼝对sql语句进⾏预编译,在很早之前还可以使⽤Statement接⼝,除此之外还需要使⽤ResultSet接口取得结果集(就是原始的数据)。
以上是我们在之前操作数据库需要使⽤到的接⼝以及相关的类,如果要带条件进⾏操作数据,需要使⽤⼀堆的setXXX⽅法设置占位符(“?”)的内容,查询到数据之后还需要使⽤ResultSet的getXXX⽅法取得原始的数据,之后再调⽤vo对象的setXXX⽅法保存数据,⽽且以上的设置和取得操作是重复的。
经过以上的分析发现传统的开发存在的弊端,于是⼀些框架就出现来解决这⼀问题,这些框架统⼀叫做对象映射框架,将数据表中的数据直接和vo类对象直接对应起来,为⽤户省去了传统开发中的那⼀堆复杂的操作。
其中最著名的是Hibernate和Mybatis(前身是ibatis),使⽤这些框架将上⾯所使⽤到的类和接⼝封装,以及参数的设置和数据的取得封装,我们只需要编写sql语句执⾏即可,如果要查询数据他会⾃动返回你要查询的对象
- Mybatis:是⼀个轻量级的框架,使⽤⽐较灵活(还可以使⽤注解实现),⽽且性能⽐Hibernate⾼。
- mybatis-plus:和mybatis进⾏互补,使⽤更⽅便。
- 有部分公司使⽤的是⾃⼰定义的框架,思想都类似
二、Mybatis的配置
- 创建一个Maven项目
- 使⽤maven(pom.xml)下载mybaits的开发包,可以在maven仓库中下载。
<dependencies>
<!--数据库连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.43</version>
</dependency>
<!--mybatis的开发包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--日志打印的依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
- 如果要使⽤mybatis开发,那么还需要⼀个主配置⽂件,将该配置⽂件拷⻉到项⽬的resource⽬录下。
mybatis.cfg.xml(放到项⽬的resource⽬录下)该配置⽂件可以到官⽹去找也可以到之前的项⽬中拷⻉。
<?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>
<settings>
<!--标准的日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 为类型配置别名
配置之后就能在映射配置文件中直接使用Emp,而不用在填写全路径cn.qf.vo.Emp
-->
<typeAliases>
<!--方式1.缺点,有很多个实例类型时需要配置很多-->
<!--<typeAlias type="cn.qf.vo.Emp" alias="Emp"/>-->
<!--方式2:以包为单位 只要在指定包下的类型,都可以舍去包名称直接应用
配置了以上节点之后,只要在该包下的所有类型都会默认给出一个自己别名:这个别名和类名称一致。
-->
<package name="cn.qf.vo"/>
</typeAliases>
<environments default="development"> <!--在该节点下配置数据库连接的信息-->
<environment id="development"> <!--id是唯⼀的,表示可以在该配置⽂件中配置多个数据库
连接信息(⼀般情况是不会配置多个的)-->
<transactionManager type="jdbc"/> <!--事务的管理⽅式使⽤的原始的数据库的管理⽅式,
在以后需要事务的管理交给Spring进⾏管理-->
<dataSource type="POOLED"> <!--表示数据库的连接交给数据库连接池管理-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.226.128:3306/TestLiu?useSSL=false&characterEncoding=UTF-8" />
<property name="username" value="root"/>
<property name="password" value="Lcj1024.."/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/EmpMapper.xml"></mapper>
</mappers>
</configuration>
- 数据库连接池:访问数据库的时候需要取得数据库的连接对象,在之前使⽤完毕之后就关闭了,如果再需要就从新取得,在这⼀过程中需要耗费⼤量的时间,这种⽅式效率⽐较低,最好的⽅式是在项⽬启动的时候先创建出⼀定数量的数据库连接对象保存到连接池,需要使⽤的时候直接从连接池中取得,使⽤完毕之后在放回连接池,试⽤这样的⽅式提⾼了数据的访问速度,数据库连接池有多种,本次demo中使⽤mybatis⾃带的连接池,市⾯上还有C3P0、DBCP、PROXOOL、DRUID。
- 编写映射⽂件,每⼀个数据表都会对应⼀个映射⽂件进⾏数据的操作,在该映射⽂件中编写sql语句,会⾃动执⾏。该映射⽂件必须定义命名空间,作⽤是定位要执⾏的sql。
目录:resources / mapper / EmpMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTDMapper3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="EmpNs"> <!--命名空间为EmpNs,后⾯操作数据的时候要使⽤到-->
</mapper>
- 编写vo对象的类
/**
1.序列化接口的继承
2.cloneable 深拷贝的实现
*/
implements Serializable,Cloneable
private、get/set、toString、构造器
7. 在主⽅法中使⽤mybatis操作数据
- 在之前要使⽤DriverManager取得Connetion对象,现在我们要使⽤SqlSessionFactory代替
- 在之前要使⽤Connection对象进⾏数据的操作,现在使⽤SqlSession代理对象完成数据的操作
//加载mybatis的主配置文件
Reader reader = Resource.getResourceAsReader("mybatis.cfg.xml");
//取得连接工厂(代替了之前的 DriverManager 取得 Connection 对象),该工厂的作用是用来取得连接
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//取得连接对象(但是该连接对象封装了之前的Connection)
SqlSession sqlSession = sqlSessionFactoy.openSession();
- 修改映射⽂件增加根据编号查询数据的操作
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTDMapper3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="EmpNs">
<!--命名空间为EmpNs,后⾯操作数据的时候要使⽤到-->
<!-- resultType 返回值元素类型 -->
<select id="selectById" resultType="cn.qf.vo.Emp"> <!-- 实体类path -->
SELECT * FROM emp WHERE id =#{0}
</select>
<!-- 根据编号修改数据 SET字段直接#{属性名} -->
<!-- parameterType 元素类型 -->
<update id="updateById" parameterType="cn.qf.vo.Emp">
UPDATE emp set name=#{name},deptid=#{deptid},salary=#{salary} where id=#{id}
</update>
<!--根据编号删除-->
<delete id="deleteById" >
delete * from emp where id=#{0}
</delete>
<!--增加数据-->
<insert id="insertEmp" parameterType="cn.qf.vo.Emp">
insert into emp(id,`name`,deptid,gender,salary,address)
values (#{id},#{name},#{deptid},#{gender},#{salary},#{address})
</insert>
<!--模糊分页查询-->
<select id="selectAllSplit" resultType="cn.qf.vo.Emp">
select * from emp where `name` like #{kw} LIMIT #{static},#{ls}
</select>
</mapper>
- 在主方法中使用Mybatis操作数据java查询—注:mybatis的事务默认是不提交,需要我们⾃⼰提交·提交事务(以后和spring整合之后就可以自动提交了)
/** 在工具类中完成!!*/
/**加载mybatis的主配置文件*/
Reader reader = Resources.getResourceAsReader("mybatis.cfg.xml");
/**取得连接⼯⼚(代替了之前的DriverManager取得Connection对象),该⼯⼚的作⽤是⽤来取得连接*/
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
/**取得连接对象(但是该连接对象封装了之前的Connection)*/
SqlSession sqlSession = sessionFactory.openSession();
//查询:
Emp emp = sqlSession.selectOne("EmpNs.selectById", 8);
//更新
public static int updateByid(SqlSession sqlSession){
Emp emp = new Emp();
emp.setId(8);
emp.setName("小刘");
emp.setDeptid(1);
emp.setSalary(5000.0);
int update = sqlSession.update("EmpNs.updateById",emp);
/** 需要手动提交事务 */
sqlSession.commit();
return update;
}
三、使用Mybatis实现数据的CRUD
1.实体类属性名和数据表的字段名映射
发现了如果实体类中的属性名和对应的数据表中字段名不一致
- 在对应的映射文件中增加查询的方法
<select id="selectAll" resultType="cn.qf.vo.Emp">
select * from emp
</select>
2.实现数据的增删改查
/* 执行update的方法: */
public static int updateByid(SqlSession sqlSession){
Emp emp = new Emp();
emp.setId(8);
emp.setName("小刘");
emp.setDeptid(1);
emp.setSalary(5000.0);
int update = sqlSession.update("EmpNs.updateById",emp);
/** 需要手动提交事务 */
sqlSession.commit();
return update;
}
/* 查询所有 */
public static List<Emp> selectAll(SqlSession sqlSession){
List<Emp> list = sqlSession.selectList("EmpNs.selectAll");
sqlSession.commit();
return list;
}
/*insert 增加*/
public static void insertEmp(SqlSession sqlSession){
Emp emp = new Emp();
emp.setId(12);
emp.setName("苏苏");
emp.setSalary(8552.0);
emp.setDeptid(3);
emp.setAddress("中国");
emp.setGender("女");
int insert = sqlSession.insert("EmpNs.insertEmp", emp);
sqlSession.commit();
System.out.println("影响行数:"+insert);
}
/* 根据id删除 */
public static void deleteById(SqlSession sqlSession){
int delete = sqlSession.delete("EmpNs.deleteById", 1);
sqlSession.commit();
System.out.println("影响行数:"+delete);
}
/* 模糊分页查询 */
public static List<Emp> selectAllSplit(SqlSession sqlSession){
/** 构建Map对象,键值对保存参数 */
Map<String, Object> map = new HashMap<String, Object>();
map.put("kw","%女%");
/**
* 形参可设置成页数
* 当为页数是,map中put("static",(页数-1)*ls )
* */
map.put("static",1);
map.put("ls",3);
List<Emp> list = sqlSession.selectList("EmpNs.selectAllSplit",map);
return list;
}
3.参数类型别名的配置
在之前我们插入一条数据的时候需要在insert节点中指定传递的参数的类型,使用包全名称+类型名指定。
<!--如果实体类中的属性名和对应的数据表中字段名不一致-->
<resultMap id="EmpMap" type="cn.qf.vo.Emp">
<result column="name" property="empName"></result>
<result column="job" property="empJob"></result>
</resultMap>
使用以上的方式会存在一些问题,如果类型比较多的时候不方便管理而且编写比较麻烦,于是我们可以在主配置文件中统一为每个类型配置自己的别名,之后在映射文件中直接引用即可。
<!-- 为类型配置别名
配置之后就能在映射配置文件中直接使用Emp,而不用在填写全路径cn.qf.vo.Emp
-->
<typeAliases>
<!--方式1.缺点,有很多个实例类型时需要配置很多-->
<!--<typeAlias type="cn.qf.vo.Emp" alias="Emp"/>-->
<!--方式2:以包为单位 只要在指定包下的类型,都可以舍去包名称直接应用
配置了以上节点之后,只要在该包下的所有类型都会默认给出一个自己别名:这个别名和类名称一致。
-->
<package name="cn.qf.vo"/>
</typeAliases>
4.返回自增长的主键值
如果增加一条数据则主键会自增长,需要将这个增长后的主键的值返回。
在映射文件中
<!--插入数据的操作-->
<insert id="insertEmp" parameterType="Emp" useGeneratedKeys="true" keyProperty="empno" keyColumn="empno">
INSERT INTO emp(empno,ename,job,sal,mgr,comm,hiredate,deptno)VALUES(#{empno},#{ename},#{job},#{sal},#{mgr},#{comm},#{hiredate},#{deptno})
</insert>
<!-- 返回自增长的主键值
useGeneratedKeys="true" keyProperty="empno"指定主键列名称 keyColumn="empno"
主键值会自动返回至该对象的id中
-->
public static void insertEmp(SqlSession session){
Emp emp=new Emp();
emp.setEname("阿超同学");
emp.setJob("高级Java工程师");
emp.setSal(9999.0);
//取得empno的值
System.out.println("插入到数据库之前:"+emp.getEmpno());
//插入数据
int row=session.insert("EmpNs.insertEmp",emp);
//提交事务,插入数据后主键会返回给该对象对应的属性值中
session.commit();
System.out.println("插入到数据库之后:"+emp.getEmpno());
System.out.println("插入了"+row+"条数据");}
- keyProperty=“empno”:指定主键列名称返回的自动增长的主键值保存到原始的vo对象对应的属性中。
5.打印日志信息
在开发中很可能需要观察sql语句的执行过程,包括语句、执行的时间、以及传递的参数、查询到的数据等等信息,此时可以使用日志的方式进行打印出来,但是需要增加一个包"log4j.jar",此外还需要将一个资源文件拷贝到resource目录下,这个配置文件是**“log4j.properties”**。
pom.xml中
<!--日志打印的依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
resource目录下,配置文件"log4j.properties"
- log4j.logger.EmpNs=TRACE:表示要对指定的命名空间中的所有sql语句进行日志跟踪
# For JBoss: Avoid to setup Log4J outside $JBOSS_HOME/server/default/deploy/log4j.xml!
# For all other servers: Comment out the Log4J listener in web.xml to activate Log4J.
log4j.rootLogger=INFO, stdout, logfile
log4j.logger.EmpNS=TRACE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=logs/my.log
log4j.appender.logfile.MaxFileSize=512KB
# Keep three backup files.
log4j.appender.logfile.MaxBackupIndex=3
# Pattern to output: date priority [category] - message
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
- Preparing:后面出现的信息是sql语句
- Parameters:后面的信息是执行sql语句的时候传递的参数以及类型
- Updates:受到影响的行数
- Columns:表示查询的时候表的字段名
- Row:查询到的结果数据
- Total:1:查询到的数量
如果日志无法打印:还需要在主配置文件中(数据库)mybatis.cfg.xml中配置
<settings>
<!--标准的日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
四、连接工具类的设计
在之前我们要取得SqlSession对象进行数据的操作,之前是在主方法中编写这一过程,我们说过主方法就是个客户端,要求客户端的代码尽可能的简单,此时我们应该将SqlSession获取的过程封装起来,就是要编写一个工具类。
package cn.qf.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
public class SqlSessionUtil {
//定义一个线程本地对象(保存SqlSession对象)
private static final ThreadLocal<SqlSession> THREAD_LOCAL = new ThreadLocal<SqlSession>();
//声明一个Reader 对象
private static Reader reader;
//声明一个工厂变量
private static SqlSessionFactory sqlSessionFactory;
//声明一个SqlSession变量
private static SqlSession sqlSession;
//在静态代码块中加载配置文件,同时获取工厂对象
static{
try { /*资源*/
reader = Resources.getResourceAsReader("mybatis.cfg.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取session对象的方法
* @return
* */
public static SqlSession getSqlSession(){
//从本地线程对象中去获取session对象
sqlSession = THREAD_LOCAL.get();
if(sqlSession==null){
//使用工厂创建session对象
/** true:自动提交事务,sqlSession.commit() */
sqlSession = sqlSessionFactory.openSession(true);
//保存到本地线程
THREAD_LOCAL.set(sqlSession);
}
return sqlSession;
}
/**
* 关闭相关资源
* */
public static void close(){
sqlSession = THREAD_LOCAL.get();
if(sqlSession!=null) sqlSession.close();
if(reader!=null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
THREAD_LOCAL.set(null);
}
}
五、使用Mybatis开发数据层
mybatis就是用来简化传统的jdbc数据层开发的。
因为事务的处理(事务的提交和回滚等操作)和session的关闭是在业务层完成的,所以本次课程中不考虑session的关闭和提交操作。
- 持久层Dao层父接口
//k是操作的实体类中表示数据表主键值的 站位标记
//T 表示要操作的实体类的类型
public interface IDAO<K,T> {
/**
* 增加数据的方法
* @param t 要添加数据的对象
* @return 成功增加的行数
* @throws Exception
*/
int insert(T t) throws Exception;
/**
* 根据编号修改数据
* @param t
* @return 成功修改的行数
* @throws Exception
*/
int update(T t) throws Exception;
/**
* 根据编号删除数据
* @param k
* @return 成功删除的行数
* @throws Exception
*/
int delete(K k) throws Exception;
/**
* 批量删除数据
* @param ids 保存了要删除的数据的编号的集合
* @return 成功删除的行数
* @throws Exception
*/
int delete(Set<K> ids) throws Exception;
/**
* 根据编号查询数据
* @param k 要查询的数据的编号
* @return 如果数据返回对象 否则返回null
* @throws Exception
*/
T selectById(K k) throws Exception;
/**
* 模糊分也查询
* @param map 保存了查询条件的集合
* @return
* @throws Exception
*/
List<T> selectSplit(Map<String,Object> map) throws Exception;
/**
* 实现数据量的统计
* @param kw 模糊查询的关键字(根据商品的名字进行模糊分页查询)
* @return
* @throws Exception
*/
int count(String kw) throws Exception;
}
- 持久层Dao层子接口
public interface IEmpDAO extends IDAO<Integer, Emp>{}
- 持久层Dao层impl实现类
public class EmpDaoImpl implements IEmpDao {
/** 在service层完成
//取得连接对象
//SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//要调用dao层的方法
*/
//Session对象
private SqlSession sqlSession;
public EmpDaoImpl(){}
public EmpDaoImpl(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public int insert(Emp emp) {
return sqlSession.insert("EmpNs.insertEmp",emp);
}
@Override
public int updateById(Emp emp) {
return sqlSession.update("updateById",emp);
}
@Override
public int delete(Integer id) {
return sqlSession.delete("EmpNs.deleteById",id);
}
@Override
/**
* delete from emp where id = in(#{0},#{1},#{2} ??????多个值不确定? 怎么写映射);
* */
public int delete(Set<Integer> ids) {
return 0;
}
@Override
public Emp selectById(Integer id) {
return sqlSession.selectOne("EmpNs.selectById",id);
}
@Override
public List<Integer> selectSplit(Map<String, Object> map) {
return sqlSession.selectList("EmpNs.selectAllSplit",map);
}
}
- 业务层service接口
public interface IEmpService {
/**
* 根据编号查询数据
* @param id
* @return
* @throws Exception
*/
Emp getById(Integer id) throws Exception;
- 业务层service实现
public class EmpServiceImpl implements IEmpService {
SqlSession session= SessionUtil.getSession();
//取的dao层实现类对象
IEmpDAO empDAO=new EmpDAOImpl(session);
public Emp getById(Integer id) throws Exception {
try{
return empDAO.selectById(id);
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭资源
SessionUtil.close();
}
return null;
}
}
六、注解开发
MyBatis可以使用注解替代映射文件,还可以省去dao层的实现类。映射文件的作用就是定义Sql语句,可以在持久层接口上使用@Select/@Delete/@Insert/@Update定义Sql语句,这样就不需要使用映射文件了,注解开发底层还是会转换为xml映射文件的方式。
很多企业还是习惯使用映射文件。
如果要使用注解开发,首先你的项目必须要能扫描到注解,需要进行配置(mybatis的核心配置文件中进行调整)
<mappers>
<!-- 映射文件地址
<mapper resource="mapper/EmpMapper.xml"></mapper>-->
<!--此时可以扫描到包中的持久层接口上的注解-->
<package name="cn.qf.mapper"></package>
</mappers>
- 持久层的父接口(采用注解开发就不需要持久层的实现,直接在业务层使用)
public interface IMapper<K,T> {
@Insert("INSERT INTO emp(empno,ename,job,sal,hiredate,deptno,comm,mgr)" +
" values (#{empno},#{ename},#{job},#{sal},#{hiredate},#{deptno},#{comm},#{mgr})")
@Options(useGeneratedKeys = true,keyProperty = "empno",keyColumn = "empno")
//满足返回自增长健
int insert(T t);
@Update("UPDATE emp SET ename=#{ename},job=#{job},sal=#{sal},hiredate=#{hiredate},deptno=#{deptno},comm=#{comm},mgr=#{mgr}) WHERE empno=#{empno}")
int update(T t);
//根据编号删除
@Delete("DELETE FROM emp WHERE empno=#{empno}")
int delete(K k);
/**
* 批量删除
* @paramids 保存了要删除的数据编号
* @return
* */
int delete(Set<K> ids);
/**
* 根据编号查询数据
* @paramk
* @return
* */
@Select("SELECT * FROM emp WHERE empno=#{0}")
T selectById(K k);
/**
* 模糊分页查询
* @parammap
* @return
* */
@Select("SELECT * FROM emp WHERE ename LIKE #{kw} LIMIT #{static},#{ls}")
List<T> selectAllSplit(Map<String,Object> map);
/**
* 统计数据量
* @paramkw
* @return
* */
@Select("SELECT COUNT(*) FROM emp WHERE ename LIKE #{0}")
int count(String kw);
}
- 业务层的实现方式
public class Service {
/**取得sqlSession对象*/
static SqlSession sqlSession = SqlSessionUtil.getSqlSession();
/**传递持久层的接口的class类对象*/
//sqlSession.getMapper(IMapper.class),可以自动为IEmpDAO接口创建一个实现类对象
private static IMapper empMapper = sqlSession.getMapper(IMapper .class);
public static void main(String[] args) {
int count = empMapper.count("%a%");
System.out.println("count统计总数为:"+count);
Emp emp = empMapper.selectById(1001);
System.out.println(emp);
}
}
七.缓存的概念
缓存就是提升查询性能的一种手段,在查询的时候先到缓存中去查找数据,如果缓存中已经存在了数据则不再发送sql语句,如果不存在则发送sql语句到数据库查询数据。
缓存又分为一级缓存和二级缓存:
- 一级缓存:在Mybatis和Hibernate中都默认支持一级缓存,一级缓存只能实现同一个用户(Session)的数据共享,所以一级缓存是session级别的缓存,同一个session多次查询同一条数据的时候,第一次从数据库中(缓存中没有对应数据的情况下),后面的从缓存中查询。
- 二级缓存:二级缓存可以实现多个用户(不同的session)之间的数据共享,在Mybatis中SqlSession是从SqlSessionFactory中取得,所以二级缓存叫做工厂级别的缓存,要实现二级缓存需要进行配置。
public static void main(String[]args)throwsException{
//取得业务层的实现类对象
EmpServiceImpl empService = new EmpServiceImpl();
//根据编号查询数据
Empemp1=empService.getById(7521);
System.out.println(emp1);
System.out.println("============================================");
//查询第二次(观察是否发送sql语句)
Empemp2=empService.getById(7521);
System.out.println(emp2);
}
结果
==> Preparing: SELECT * FROM emp WHERE empno =?
==> Parameters: 7876(Integer)
<== Columns: empno, ename, pwd, job, mgr, hiredate, sal, comm, deptno, create_time, update_time, version, deleted
<== Row: 7876, ADAMS, null, CLERK, 7788, 1983-01-12 00:00:00.0, 1100.00, null, 20, null, null, null, 0
<== Total: 1
Emp{empno=7876, ename='ADAMS', job='CLERK', sal=1100.0, hiredate=Wed Jan 12 00:00:00 CST 1983, deptno=20, comm=null, mgr=7788}
======================================
Emp{empno=7876, ename='ADAMS', job='CLERK', sal=1100.0, hiredate=Wed Jan 12 00:00:00 CST 1983, deptno=20, comm=null, mgr=7788}
发现第二次查询同一个数据的时候不会发送sql语句,而是从缓存取得数据。
-
两个session,结果发现就算两个session查询的是同一条数据也不能实现数据的共享,如果要在不同的用户之间实现数据共享需要使用二级缓存。
在mybatis的主配置文件中进行配置
<settings>
<!--不同的用户之间实现数据共享需要使用二级缓存-->
<setting name="cacheEnavled" value="true"/>
</settings>
在需要缓存的映射文件中进行配置
<cache eviction="LRU" flushInterval="1000" size="500" readOnly="true"></cache>
eviction表示缓存的管理策略,管理策略有四种:
eviction=“LRU”:表示最近使用最少的对象清空
eviction=“FIFO”:表示清空最先进入缓存的
eviction=“SOFT”:按照软引用的方式进行管理,只有在内存不足的时候进行清空
eviction=“WEAK”:按照弱引用的方式进行管理,只要执行GC就进行清空我们常用的是"LRU"
flushInterval=“1000”:表示一秒刷新一次
size:表示的可以缓存对象的最大个数
readOnly=“true”:表示缓存只能读取不能修改
二级缓存
@Test
public void test01() {
EmpServiceImpl empService = new EmpServiceImpl();
//第一次查询
Emp emp = empService.selectById(7876);
System.out.println(emp);
System.out.println("===========" + empService.sqlSession);
SqlSessionUtil.close(); //关闭当前sqlSession,重新申请sqlSession查询
System.out.println("======================================");
//第二次查询
EmpServiceImpl empService2 = new EmpServiceImpl(); //重新创建sqlSession
Emp emp1 = empService2.selectById(7876);
System.out.println("===========" + empService2.sqlSession);
System.out.println(emp1);
}
结果
==> Preparing: SELECT * FROM emp WHERE empno =?
==> Parameters: 7876(Integer)
<== Columns: empno, ename, pwd, job, mgr, hiredate, sal, comm, deptno, create_time, update_time, version, deleted
<== Row: 7876, ADAMS, null, CLERK, 7788, 1983-01-12 00:00:00.0, 1100.00, null, 20, null, null, null, 0
<== Total: 1
Emp{empno=7876, ename='ADAMS', job='CLERK', sal=1100.0, hiredate=Wed Jan 12 00:00:00 CST 1983, deptno=20, comm=null, mgr=7788}
/** sqlSession对象1 */
==org.apache.ibatis.session.defaults.DefaultSqlSession@473b46c3
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@11c20519]
Returned connection 297927961 to pool.
======================================
Cache Hit Ratio [EmpNs]: 0.5
/** sqlSession对象2 */
==org.apache.ibatis.session.defaults.DefaultSqlSession@516be40f
Emp{empno=7876, ename='ADAMS', job='CLERK', sal=1100.0, hiredate=Wed Jan 12 00:00:00 CST 1983, deptno=20, comm=null, mgr=7788}
此时就使用二级缓存实现了数据查询性能的提高,
0.5表示查询了两个对象,其中有1个是从缓存中获取
0.6666表示查询了3个对象,其中有2个是从缓存中获取。
1、使用二级缓存你要特别小心
2、二级缓存只能在单表操作中考虑使用
3、只有在数据更新频率很小的时候考虑使用
4、二级缓存要生效查询也要进行事务的提交。
八、一对多的关联配置
每个部门有多个雇员,所以这就是一对多关系,部门叫做一方,雇员叫做多方。
在查询数据的时候可能要进行关联查询,查询部门信息的时候要把它所有的雇员查询出来,在查询雇员的时候也需要把对应的部门信息查询出来,这种操作就叫做关联查询。
在传统的开发(ibatis刚出来的时候)中使用的是mybatis提供的方案,需要配置association,collection等节点,但是配置的时候非常繁琐,不方便使用起来很麻烦,所以不推荐使用,遇到一对多的情况就直接在java程序中处理了。
- 分析一对多的关联配置(使用新的方案解决)
- 编写Dept的vo类
public class Dept implements Serializable,Cloneable{
private Integer deptno;
private String dname;
private String loc;
//保存部门的所有雇员的集合
private List<Emp> empList = new ArrayList<Emp>(30);
}
- 修改Emp的vo类,增加一个表示所部门的属性
- 配置DeptMapper.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTDMapper3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="DeptNs">
<select id="findDeptById" resultType="Dept">
SELECT * FROM dept WHERE deptno=#{0}
</select>
</mapper>
- 在主配置文件(mybatis.cfg.xml)中指定新的mapper文件(DeptMapper.xml)
<mappers>
<!-- 映射文件地址-->
<mapper resource="mapper/EmpMapper.xml"></mapper>
<mapper resource="mapper/DeptMapper.xml"></mapper>
<!--此时可以扫描到包中的持久层接口上的注解
<package name="cn.qf.mapper"></package>-->
</mappers>
/**-关联配置--根据部门的编号查询部门信息*/
public Dept selectById(Integer deptno) {
//查询部门
Dept dept = deptDao.selectById(deptno);
//查询部门雇员性息
List<Emp> list = sqlSession.selectList("selectEmpsByDeptno", deptno);
dept.setEmpList(list);
return dept;
}
@Test
public void test01(){
IDeptService deptService = new DeptServiceImpl();
Dept dept = deptService.selectById(10);
System.out.println(dept);
}
//-----------------------------------------
/**
* - 关联查询 -
* 查询出雇员的信息,
* 之后还要查询雇员所在的部门信息
*
* */
public Map<String, Object> selectAll() {
/**
* 模糊查询 kw 字段参数需要加~~~~~~‘单引号’~~~~~~~~
* */
int count = empDao.count("'%'"); //总数
Map<String,Object> map = new HashMap();
//查询所有雇员
List<Emp> emps = empDao.selectAll();
//为每个雇员查询部门信息
emps.forEach(new Consumer<Emp>() {
@Override
public void accept(Emp emp) {
Dept dept = sqlSession.selectOne("DeptNs.findDeptById", emp.getDeptno());
emp.setDept(dept);
}
});
map.put("emps",emps);
map.put("size",count);
return map;
}
@Test
public void test02(){
IEmpService empService = new EmpServiceImpl();
Map<String, Object> emps = empService.selectAll();
System.out.println(emps);
}
总结:
- 以上就是一对多的新方案查询(使用java程序处理)
- 还有多对多的关系,也使用java程序处理,处理的思路和一对多差不多的。
九、Mybatis中的动态语句
在很多时候我们查询数据会有多个条件,那么这些条件是不固定的,此时就需要对条件判断之后根据不同的情况使用不同的sql。
- 方案一:参数判断之后给了默认的处理 或者 dao层判断,判断之后使用不同的sql语句。
- 方案二:现在可以在Mybatis的映射文件实现判断。
9.1 动态聚合函数《if test》
<!--实现数据量的统计-->
<select id="selectCount" resultType="Integer">
select count(*) from emp where 1=1
<if test="kw!=null">
and ename LIKE ${kw}
</if>
</select>
/**
* 动态条件查询---聚合函数
**/
@Test
public void test03(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
Integer count = sqlSession.selectOne("EmpNs.selectCount",null);
System.out.println(count); //12
/**
* 模糊查询 kw 字段参数需要加~~~~~~‘单引号’~~~~~~~~
* */
Integer count2 = sqlSession.selectOne("EmpNs.selectCount", "'%a%'");
System.out.println(count2); //5
}
- 注解开发:Mybatis动态语句if标签示例:
@Select(
/** databaseId = "mysql", */
value="<script>" +
"select id,name,create_time,enabled,locked,unlock_time," +
"zydm,gsdm," +
"(select bmmc from PISP_BMXX where bmdm in (select bmdm from PISP_ZYXX where ZYDM = PISP_USER.zydm) and gsdm = PISP_USER.gsdm limit 1) bmmc " +
" from PISP_USER " +
"<if test='type == \"01\"'>" +
" where ifnull(zydm,'') ='' " +
"</if>" +
"<if test='type == \"02\" and gsdm == \"\"'>" +
" where ifnull(zydm,'') <>'' " +
"</if>" +
"<if test='type == \"02\" and gsdm != \"\" and bmdm == \"\"'>" +
" where ifnull(zydm,'') <>'' and gsdm = #{gsdm}" +
"</if>" +
"<if test='type == \"02\" and gsdm != \"\" and bmdm != \"\"'>" +
" where ifnull(zydm,'') <>'' and gsdm = #{gsdm} and zydm in (select zydm from PISP_ZYXX where gsdm = #{gsdm} and bmdm like concat(#{bmdm},'%'))" +
"</if>" +
" order by PISP_USER.id"
+ "</script>"
)
public List<User4ManagerTable> getUsersByUserType(String type, String gsdm, String bmdm);
9.2 模糊分页查询if text
<!--模糊分页查询-->
<select id="selectEmpAllSplit" resultType="cn.qf.vo.Emp">
select * from emp where 1=1
<if test="kw!=null">
and ename like ${kw}
</if>
<if test="column!=null">
order by ${column} ${sort}
</if>
<if test="start!=null">
LIMIT ${start},${ls}
</if>
</select>
/**
* 动态查询---模糊、分页、排序
* */
@Test
public void test04(){
HashMap<String, Object> map = new HashMap<>();
map.put("kw",null);
map.put("column","sal");
map.put("sort","DESC"); /** order by desc/asc(默认↑) */
map.put("start",1);
map.put("ls",4);
IEmpService empService = new EmpServiceImpl();
List<Emp> list = empService.selectEmpSplit(map);
list.forEach(System.out::println);
}
<!--动态sql更新-->
<update id="updateEmpById" parameterType="Emp">
UPDATE emp SET ename=#{ename}
<if test="job!=null">
,job=#{job}
</if>
<if test="sal!=null">
,sal=#{sal}
</if>
WHERE empno=#{empno}
</update>
/**
* 动态更新sql语句
* */
@Test
public void test05(){
Emp emp = new Emp();
emp.setEmpno(7521);
emp.setEname("老六");
emp.setSal(10000.0);
IEmpService empService = new EmpServiceImpl();
int i = empService.updateEmp(emp);
System.out.println(i);
}
9.3 多条件动态聚合函数
where》choose》when/otherwise
在查询数据的时候如果有多个限制条件,但是我们要求只要有一个满足条件了,其他条件就不需要了,此时使用 choose
when 解决,但是这种需求极少(作为了解)
- where之后多个条件只要有一个满足其他不需要
<!--动态sql更新-->
<update id="updateEmpById" parameterType="Emp">
UPDATE emp SET ename=#{ename}
<if test="job!=null">
,job=#{job}
</if>
<if test="sal!=null">
,sal=#{sal}
</if>
<where>
<choose>
<when test="sal!=null">
and sal < 0 <!-- 小于 -->
</when>
<when test="job!=null">
1=2;
</when>
<otherwise>
empno=#{empno}
</otherwise>
</choose>
</where>
</update>
@Test
public void test06(){
Emp emp = new Emp();
emp.setEmpno(7521);
emp.setEname("老六");
emp.setSal(10000.0);
IEmpService empService = new EmpServiceImpl();
int i = empService.updateEmp(emp);
System.out.println(i);
}
9.4 批量删除数据
在开发中一定会遇到批量删除数据的需求,在之前我们使用jdbc的时候是在jdbc中对sql语句进行组装,现在我们使用了mybatis 有对应的组装sql的方式。
<!-- 批量删除 -->
<delete id="deleteByIdList">
delete from emp WHERE empno IN
<foreach collection="list" open="(" separator="," close=")" item="empno">
#{empno}
</foreach>
</delete>
/* 批量删除 */
@Test
public void test07(){
IEmpService empService = new EmpServiceImpl();
ArrayList<Integer> ids = new ArrayList<>();
ids.add(1001);
ids.add(7521);
ids.add(7698);
ids.add(7844);
int row = empService.deleteByIdList(ids);
System.out.println("删除影响行数:"+row);
}
- 注解开发,Mybatis动态Sql foreach 标签示例:
@Insert(
value = {
"<script>"+
"INSERT INTO pisp_role_menu(role_id,menu_code,create_time,id) VALUES "+
"<foreach collection='list' separator=',' item='item'>" +
"(#{roleId},#{item.menuCode},TO_CHAR(current_timestamp,'YYYYMMDD'),#{item.id})"+
"</foreach>"+
"</script>"
}
)
public int addMenusTestkingbase(int roleId, @Param("list") List<Map> list);
Mean整合Web(tomCat)
_数据库Mybatis开发!
1.创建项目
搭建环境:Maven项目模板下选择 maven-archetype-webapp
其余和基础Maven的配置方式相同
2.创建3个文件夹
- 在main下面创建java文件夹 和 resources 文件夹
- 在webapp/WEB-INF下创建classes文件(源文件–class文件–加载运行),lib文件不需要在创建,因为现在是Maven项目,所有需要的jar包直接从Maven的pom.xml文件中配置即可
3.配置项目环境
ctr+alt+shift+s
配置路径为:上一步创建的classes文件目录下
4.添加TomCat配置
- 在Deployment目录下:+添加【项目名:war exploded】
- 在Deployment目录下:Application context:/ 设置虚拟路径
5.添加相关依赖
在Maven的pom.xml文件中添加所有需要的依赖jar包
- 注:jdk的版本改为108
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
- log4j.properties – 日志打印文件
- mybatis.cfg.xml – mybatis开发,那么还需要⼀个主配置⽂件
- mapper中的文件为 mybatis开发对应的映射文件
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
</dependency>
<!-- servlet注解开发需要的jar文件
https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--mybatis的开发包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--数据库连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.43</version>
</dependency>
<!--日志打印的依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.4.2</version>
<scope>compile</scope>
</dependency>
<!--JSP:Jstl依赖jar-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>