一.Mybatis认识
1.1 Mybatis框架概述
-
MyBatis是一个ORM的数据库持久化框架;
-
Mybatis底层还是原生的JDBC代码,对JDBC代码的封装;
1.2 数据库持久化
-
数据持久化就是将内存中的数据模型转换为存储模型,即把内存中的数据保存到数据库中;
-
常见的数据持久化分为磁盘持久化和数据库持久化;
-
Java中最简单的就是使用原生的jdbc代码来完成数据库持久化,但是这种方式有很多问题;
1.3 什么是ORM
- ORM:对象关系映射(Object Relational Mapping,简称ORM):是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术;
1.4 ORM框架映射方式
- 第一种:
- Sql操作方式(半映射或半自动):把SQL配置到配置文件中,通过不同SQL中完成对象实体和数据库关系相互转换的操作(Mybatis的实现方式)
- 第二种:
- 完整映射:直接使用对象实体和数据库关系进行映射,不用写SQL(简单的操作),由框架自己生成(JPA、Hibenate实现方式)
二. Mybatis项目搭建准备
- 导入Jar包
- 数据库连接:
mysql-connector-java-5.1.26-bin.jar
- mybatis核心包:
mybatis-3.2.1.jar
- 其他依赖包
- asm-3.3.1.jar
- cglib-2.2.2.jar
- commons-logging-1.1.1.jar
- javassist-3.17.1-GA.jar
- log4j-1.2.17.jar
- slf4j-api-1.7.2.jar
- slf4j-log4j12-1.7.2.jar
- 数据库连接:
- 创建项目结构
- domain
- Product.java
- ProductMapper.xml
- dao
- IProductDao.java
- impl
- ProductDaoImpl
- test
- TestProductDao.java
- util
- MybatisUtil.java
- lib文件夹(用于存放jar包)
- …jar包
- resource资源文件夹(用于存放配置文件)
- Configeration.xml
- db.properties
- domain
三. Mybatis项目搭建
3.1 创建配置文件
-
创建一个resource文件夹,创建db.properties和Configeration.xml文件
-
db.properties
-
#驱动全限定类名 driverClassName=com.mysql.jdbc.Driver #数据库连接地址 url=jdbc:mysql://127.0.0.1:3306/数据库名字 #数据库用户名 username=你的用户名 #数据库密码 password=你的数据库密码
-
Configeration.xml
-
<?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> <!-- properties标签中 resource属性中存放配置文件名 --> <properties resource="db.properties"></properties> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${driverClassName}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </dataSource> </environment> </environments> <!-- mappers存放想要映射的配置文件 --> <mappers> <mapper resource="cn/xxxx/domain/ProductMapper.xml" /> </mappers> </configuration>
3.2 创建domain层
-
创建一个类
Product
,(JavaBean)生成Getters和Setters方法,(toString方法)用于测试 -
package cn.xxxx.domain; import java.math.BigDecimal; public class Product { private Long id; private String productName; private Long dir_id; private BigDecimal salePrice; private String supplier; private String brand; private Double cutoff; private BigDecimal costPrice; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public Long getDir_id() { return dir_id; } public void setDir_id(Long dir_id) { this.dir_id = dir_id; } public BigDecimal getSalePrice() { return salePrice; } public void setSalePrice(BigDecimal salePrice) { this.salePrice = salePrice; } public String getSupplier() { return supplier; } public void setSupplier(String supplier) { this.supplier = supplier; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public Double getCutoff() { return cutoff; } public void setCutoff(Double cutoff) { this.cutoff = cutoff; } public BigDecimal getCostPrice() { return costPrice; } public void setCostPrice(BigDecimal costPrice) { this.costPrice = costPrice; } @Override public String toString() { return "Product [id=" + id + ", productName=" + productName + ", dir_id=" + dir_id + ", salePrice=" + salePrice + ", supplier=" + supplier + ", brand=" + brand + ", cutoff=" + cutoff + ", costPrice=" + costPrice + "]"; } }
3.3 创建dommain层下的映射配置文件
-
创建
ProductMapper.xml
文件 -
<?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"> <!-- namespace: 命名空间:随便写,要求为cn.xxxx.dao.IProductDao 接口的完全限定名 id: 随便写:后面也有要求findOne 方法名字一致 parameterType: 接口中定义方法,参数类型传入 resultType : 表中每一行的结果类型 #{id} 接收方法的参数 --> <mapper namespace="cn.xxxx.dao.IProductDao"> <select id="queryOne" parameterType="long" resultType="cn.xxxx.domain.Product"> select * from product where id = #{id} </select> <select id="queryAll" resultType="cn.xxxx.domain.Product"> select * from product </select> </mapper>
3.4 创建dao层
-
创建
IProductDao
接口 -
import java.util.List; import cn.xxxx.domain.Product; public interface IProductDao { /**商品添加方法*/ void add(Product product); /**商品修改方法*/ void update(Product product); /**商品删除方法*/ void delete(Long id); /**商品查询方法(一个)*/ Product queryOne(Long id); /**商品查询方法(全部)*/ List<Product> queryAll(); }
-
创建接口的实现类
-
ProductDaoImpl
-
import java.io.IOException; import java.io.Reader; import java.util.List; 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 cn.xxxx.dao.IProductDao; import cn.xxxx.domain.Product; public class ProductDaoImpl implements IProductDao { @Override public void add(Product product) { // TODO Auto-generated method stub } @Override public void update(Product product) { // TODO Auto-generated method stub } @Override public void delete(Long id) { // TODO Auto-generated method stub } @Override public Product queryOne(Long id) { //设置resources文件夹下的配置文件名 String resource = "Configeration.xml"; //获取一个Reader的对象read Reader reader = null; try { //读取配置文件 reader = Resources.getResourceAsReader(resource); //使用SqlSessionFactoryBuilder().build(Reader)方法获取SqlSessionFactory对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); //使用SqlSessionFactory下的openSession方法来获取SqlSession对象 SqlSession session = factory.openSession(); //通过配置文件执行sql操作 session.selectOne("配置文件中设置的namespace.配置文件中select标签的id值",需要传入的参数); Product product = session.selectOne("cn.xxxx.domain.ProductMapper.queryOne",id); return product; } catch (IOException e) { e.printStackTrace(); } return null; } @Override public List<Product> queryAll() { // TODO Auto-generated method stub return null; } }
3.5 创建测试类
-
创建测试类
ProductDaoTest
-
import static org.junit.Assert.*; import org.junit.Test; import cn.xxxx.dao.IProductDao; import cn.xxxx.dao.impl.ProductDaoImpl; import cn.xxxx.domain.Product; public class ProductDaoTest { IProductDao dao = new ProductDaoImpl(); @Test public void testQueryOne() throws Exception { Product queryOne = dao.queryOne(1L); System.out.println(queryOne); } }
3.6 完善增、删、改功能
-
ProductMapper.xml
-
<?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="cn.xxxx.dao.IProductDao"> <!-- parameterType传入参数的类型 resultType输出结果的类型--> <select id="queryOne" parameterType="long" resultType="cn.xxxx.domain.Product"> select * from product where id = #{id} </select> <select id="queryAll" resultType="cn.xxxx.domain.Product"> select * from product </select> <!-- 添加sql语句 --> <insert id="add" parameterType="cn.xxxx.domain.Product"> INSERT INTO product VALUES(null,#{productName},#{dir_id},#{salePrice},#{supplier},#{brand},#{cutoff},#{costPrice}) </insert> <!-- 修改sql语句 --> <update id="update" parameterType="cn.xxxx.domain.Product"> UPDATE product set productName=#{productName}, dir_id=#{dir_id},salePrice=#{salePrice}, supplier=#{supplier},brand=#{brand}, cutoff=#{cutoff},costPrice=#{costPrice} WHERE id=#{id} </update> <!-- 删除sql语句 --> <delete id="delete" parameterType="long"> DELETE FROM product WHERE id=#{id} </delete> </mapper>
-
ProductDaoImpl
-
package cn.xxxx.dao.impl; import java.io.IOException; import java.io.Reader; import java.util.List; 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 cn.xxxx.dao.IProductDao; import cn.xxxx.domain.Product; public class ProductDaoImpl_bak implements IProductDao { @Override public void add(Product product) { String resource = "Configeration.xml"; Reader reader = null; SqlSession session =null; try { reader = Resources.getResourceAsReader(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); session = factory.openSession(); session.insert("cn.xxxx.dao.IProductDao.add",product); } catch (IOException e) { e.printStackTrace(); } session.commit(); } @Override public void update(Product product) { String resource = "Configeration.xml"; Reader reader = null; SqlSession session =null; try { reader = Resources.getResourceAsReader(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); session = factory.openSession(); session.update("cn.xxxx.dao.IProductDao.update", product); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } session.commit(); } @Override public void delete(Long id) { String resource = "Configeration.xml"; Reader reader = null; SqlSession session =null; try { reader = Resources.getResourceAsReader(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); session = factory.openSession(); session.delete("cn.xxxx.dao.IProductDao.delete", id); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } session.commit(); } @Override public Product queryOne(Long id) { //设置resources文件夹下的配置文件名 String resource = "Configeration.xml"; //获取一个Reader的对象read Reader reader = null; try { //读取配置文件 reader = Resources.getResourceAsReader(resource); //使用SqlSessionFactoryBuilder().build(Reader)方法获取SqlSessionFactory对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); //使用SqlSessionFactory下的openSession方法来获取SqlSession对象 SqlSession session = factory.openSession(); //通过配置文件执行sql操作 session.selectOne("配置文件中设置的namespace.配置文件中select标签的id值",需要传入的参数); Product product = session.selectOne("cn.xxxx.dao.IProductDao.queryOne",id); return product; } catch (IOException e) { e.printStackTrace(); } return null; } @Override public List<Product> queryAll() { String resource = "Configeration.xml"; Reader reader = null; try { reader = Resources.getResourceAsReader(resource); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = factory.openSession(); List<Product> list = session.selectList("cn.xxxx.dao.IProductDao.queryAll"); return list; } catch (IOException e) { e.printStackTrace(); } return null; } }
四. Mybatis工具类
4.1 Sqlsession对象抽取
-
因为每次进行数据库操作时都需要获取Sqlsession对象,所以编写工具类来抽取出SqlSession对象
-
import java.io.IOException; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public enum MybatisUtil { INSTANCE; //获取实例对象时执行静态代码块的方法 private static SqlSessionFactory factory = null; static{ try { factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("Configeration.xml")); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 获取SqlSession对象 * @return */ public SqlSession getSqlSession(){ return factory.openSession(); } }
4.2 ProductDaoImpl
代码优化
-
package cn.xxxx.dao.impl; import java.util.List; import org.apache.ibatis.session.SqlSession; import cn.xxxx.dao.IProductDao; import cn.xxxx.domain.Product; import cn.xxxx.util.MybatisUtil; public class ProductDaoImpl implements IProductDao { @Override public void add(Product product) { SqlSession session = null; try { session = MybatisUtil.INSTANCE.getSqlSession(); session.insert("cn.xxxx.dao.IProductDao.add",product); //事务提交 session.commit(); } catch (Exception e) { e.printStackTrace(); //事务回滚 session.rollback(); }finally { session.close(); } } @Override public void update(Product product) { SqlSession session = null; try { session = MybatisUtil.INSTANCE.getSqlSession(); session.update("cn.xxxx.dao.IProductDao.update", product); session.commit(); } catch (Exception e) { e.printStackTrace(); session.rollback(); }finally { session.close(); } } @Override public void delete(Long id) { SqlSession session = null; try { session = MybatisUtil.INSTANCE.getSqlSession(); session.delete("cn.xxxx.dao.IProductDao.delete", id); session.commit(); } catch (Exception e) { e.printStackTrace(); session.rollback(); }finally { session.close(); } } @Override public Product queryOne(Long id) { SqlSession session = null; try { session = MybatisUtil.INSTANCE.getSqlSession(); Product product = session.selectOne("cn.xxxx.dao.IProductDao.queryOne",id); return product; } catch (Exception e) { e.printStackTrace(); }finally { session.close(); } return null; } @Override public List<Product> queryAll() { SqlSession session = null; try { session = MybatisUtil.INSTANCE.getSqlSession(); List<Product> list = session.selectList("cn.xxxx.dao.IProductDao.queryAll"); return list; } catch (Exception e) { e.printStackTrace(); }finally { session.close(); } return null; } }
五.代码简化
5.1 新建ProductMapper
接口
-
新建一个Mapper包
-
package cn.xxxx.mapper; import java.util.List; import cn.xxxx.domain.Product; public interface ProductMapper { /**商品添加方法*/ void add(Product product); /**商品修改方法*/ void update(Product product); /**商品删除方法*/ void delete(Long id); /**商品查询方法(一个)*/ Product queryOne(Long id); /**商品查询方法(全部)*/ List<Product> queryAll(); }
5.2 新建ProductMapper.xml
配置文件
注意1
namespace中的值应该和ProductMapper接口的全限定名一致
注意2
select/delete/insert/update中的id属性,parameterType属性,resultType属性
应该和接口中的方法名,方法传入值类型,方法结果类型一致
<?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="cn.xxxx.mapper.ProductMapper">
<!-- parameterType传入参数的类型 resultType输出结果的类型-->
<select id="queryOne" parameterType="long" resultType="cn.xxxx.domain.Product">
select * from product where id = #{id}
</select>
<select id="queryAll" resultType="cn.xxxx.domain.Product">
select * from product
</select>
<!-- 添加sql语句 -->
<insert id="add" parameterType="cn.xxxx.domain.Product">
INSERT INTO product VALUES(null,#{productName},#{dir_id},#{salePrice},#{supplier},#{brand},#{cutoff},#{costPrice})
</insert>
<!-- 修改sql语句 -->
<update id="update" parameterType="cn.xxxx.domain.Product">
UPDATE product set productName=#{productName},
dir_id=#{dir_id},salePrice=#{salePrice},
supplier=#{supplier},brand=#{brand},
cutoff=#{cutoff},costPrice=#{costPrice}
WHERE id=#{id}
</update>
<!-- 删除sql语句 -->
<delete id="delete" parameterType="long">
DELETE FROM product WHERE id=#{id}
</delete>
</mapper>
5.3 测试类进行增删改查测试
- 使用工具类获取到
SqlSession对象
- 使用SqlSession中的
getMapper(接口字节码文件)
方法获取映射管理器 - 映射管理器
调用接口中的方法
package cn.xxxx.test;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import cn.xxxx.domain.Product;
import cn.xxxx.mapper.ProductMapper;
import cn.xxxx.util.MybatisUtil;
public class MybatisPlusTest {
SqlSession session = MybatisUtil.INSTANCE.getSqlSession();
ProductMapper mapper = session.getMapper(ProductMapper.class);
@Test
public void testQueryOne() throws Exception {
Product product = mapper.queryOne(29L);
System.out.println(product);
}
@Test
public void testDelete() throws Exception {
mapper.delete(2L);
//对数据库进行增删改查需要提交事务
session.commit();
}
@Test
public void testAdd() throws Exception {
Product product = mapper.queryOne(5L);
mapper.add(product);
session.commit();
}
@Test
public void testUpdate() throws Exception {
Product product = mapper.queryOne(5L);
product.setProductName("修改测试");
mapper.update(product);
session.commit();
}
@Test
public void testQueryAll() throws Exception {
List<Product> list = mapper.queryAll();
list.forEach(System.out::println);
}
}
六.配置文件简化
-
在
ProductMapper.xml
配置文件中,每一次使用Product类都需要写全限定名 -
在
Configeration.xml
中使用<typeAliases>
进行配置的简化 -
Configeration.xml
<?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>
<properties resource="db.properties"></properties>
<typeAliases>
<!-- 单个配置:测试时使用 -->
<!-- typeAlias type="cn.xxxx.domain.Dept" alias="Dept" -->
<!-- 包的配置:项目中使用,添加了包之后,类名或类名首字母小写就是别名 -->
<package name="cn.xxxx.domain" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/xxxx/domain/ProductMapper.xml" />
<mapper resource="cn/xxxx/mapper/ProductMapper.xml" />
</mappers>
</configuration>
七.Log日志
-
使用日志需要在
resource
文件夹中放一个log4j.properties
文件,并进行配置 -
log4j.properties
-
#日志器logger #輸出器appender #布局器layout #1.控制台输出 #指定日志器的输出级别和日志器的名称 #log4j.rootLogger=info,myconsole #指定输出器 #log4j.appender.myconsole=org.apache.log4j.ConsoleAppender #指定布局器 #log4j.appender.myconsole.layout=org.apache.log4j.SimpleLayout #2.文件输出.txt #指定日志器的输出级别和日志器的名称 #log4j.rootLogger=error,myfile #指定输出器 #log4j.appender.myfile=org.apache.log4j.FileAppender #log4j.appender.myfile.File=E:\\log4j.txt #指定布局器(普通布局表示文本输出) #log4j.appender.myfile.layout=org.apache.log4j.SimpleLayout #3.文件输出.html #指定日志器的输出级别和日志器的名称 #log4j.rootLogger=error,myhtml #指定输出器 #log4j.appender.myhtml=org.apache.log4j.FileAppender #log4j.appender.myhtml.File=D:\\log4j.html #指定布局器(网页布局) #log4j.appender.myhtml.layout=org.apache.log4j.HTMLLayout #4.控制台输出+文件输出.txt #指定日志器的输出级别和日志器的名称 #log4j.rootLogger=error,con,file #指定输出器 #log4j.appender.con=org.apache.log4j.ConsoleAppender #log4j.appender.file=org.apache.log4j.FileAppender #log4j.appender.file.File=D\:\\log4j.txt #指定布局器(网页布局) #log4j.appender.con.layout=org.apache.log4j.SimpleLayout #log4j.appender.file.layout=org.apache.log4j.SimpleLayout #5.控制台输出+自定义布局 log4j.rootLogger=DEBUG,my #指定输出器 log4j.appender.my=org.apache.log4j.ConsoleAppender #指定布局器(自定义布局) #指定布局为自定义布局 log4j.appender.my.layout=org.apache.log4j.PatternLayout #指定在自定义布局的格式,%d -- 表示当前系统时间,%t -- 执行该业务的线程名称,%p -- 日记器的级别,-5 -- 5表示输出字符的个数,符号表示右对齐 #%c -- 表示指定业务所在的类的完全限定名(包名.类名),%m -- 输出额外信息,%n -- 表示换行 log4j.appender.my.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n #设置package(可以是自定义的包也可以是api的包)输出级别 log4j.logger.org.springframework=info log4j.logger.cn.xxxx=debug