1、配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/product_systeam?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
//useSSL=true开启安全连接
//useUnicode=true 使用Unicode编码
//serverTimezone=Asia/Shanghai 设置时区
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="jdbc:mysql://localhost:3306/product_systeam?characterEncoding=utf8&useSSL=true"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
//如果在xml文件中使用mybatis配置(& 写法为 &)
2、建造mybatis工具类
package com.example.demo.productSysteam.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.InputStream;
/**
* @author wzh
* @date 2021/11/17 14:51
*/
//sqlSessionFactory --> sqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
3、编写代码
实体类
package com.example.demo.productSysteam.entity;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
*
* </p>
*
* @author wzh
* @since 2021-10-31
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 客户表 客户号
*/
private String customerNumber;
/**
* 姓名
*/
private String name;
/**
* 密码
*/
private String password;
/**
* 手机号
*/
private String phoneNumber;
/**
* 客户类型
*/
private String customerType;
/**
* 性别
*/
private String sex;
}
Dao接口
@Repository
public interface CustomerMapper extends BaseMapper<Customer> {
public List<Customer> findAll();
}
接口实现
<?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="com.example.demo.productSysteam.mapper.CustomerMapper">
<select id="findAll" resultType="customer">
select * from customer
</select>
</mapper>
测试
package com.example.demo.productSysteam.mapper;
import com.example.demo.productSysteam.entity.Customer;
import com.example.demo.productSysteam.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
/**
* @author wzh
* @date 2021/11/17 22:59
*/
public class CustomerTest {
@Test
public void test(){
/*第一步:获取sqlsession对象*/
SqlSession sqlSession = MybatisUtils.getSqlSession();
/*方式一:getmapper*/
try {
CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
List<Customer> customerList = customerMapper.findAll();
/*方法二*/
List<Customer> Customer = sqlSession.selectList("com.example.demo.productSysteam.mapper.CustomerMapper.findALL");
for (Customer customer : customerList) {
System.out.println(customer);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
遇到的问题
- 绑定接口出错
- 方法名不对
- 配置文件不全
- 返回类型不对
- mavan问题
CRUD
- namespace 包名要与Mapper/下的接口包名保持一致
<mapper namespace="com.example.demo.productSysteam.mapper.CustomerMapper">
select
id 方法名名称
resultType Sql语句结果返回值
parameterType 传入参数类型
1、编写接口
public Customer getCustomerByNumber(String customer_number);
2、编写对饮的mapper中的sql语句
<select id="getCustomerByNumber" parameterType="String" resultType="customer">
select * from customer where customer_number= #{customer_number};
</select>
3、测试
@Test
public void getCustomerByNumber(){
Customer customerByNumber = customerMapper.getCustomerByNumber("111");
System.out.println(customerByNumber);
}
insert
接口
public int addCustomer(Customer customer);
编写mapper
<insert id="addCustomer" parameterType="customer">
insert into customer(customer_number,name,password,phone_number,customer_type,sex)
values (#{customerNumber},#{name},#{password},#{phoneNumber},#{customerType},#{sex})
</insert>
测试
@Test
public void addCustomer(){
int customerByNumber = customerMapper.addCustomer(new Customer("112","冯集贸","123456","123123123","你爹","nan"));
System.out.println(customerByNumber);
}
update
编写接口
public int updataCustomerByNumber(Customer customer);
编写mapper
<update id="updataCustomerByNumber" parameterType="customer">
update customer set name = #{name},password = #{password},
phone_number = #{phoneNumber},customer_type= #{customerType},
sex = #{sex} where customer_number = #{customerNumber}
</update>
测试
@Test
public void findCustomerByNumber(){
int int1 = customerMapper.updataCustomerByNumber(new Customer("112", "冯集贸11", "123456", "1111111", "你妈", "nan"));
Customer customerByNumber = customerMapper.getCustomerByNumber("112");
System.out.println(customerByNumber);
}
delete
编写接口
//删除用户
public int deleteCustomer(String customer_number);
编写mapper.xml文件
<delete id="deleteCustomer" parameterType="String" >
delete from customer where customer_number = #{customer_number}
</delete>
测试
@Test
public void deleteCustomer(){
int i = customerMapper.deleteCustomer("112");
System.out.println(i);
}
mybatis想要提交修改需要提交事务
错误点
标签不要匹配错误
resource绑定mapper时,需要使用路径
程序配置文件必须符合规范
NullPointerException 没有注册到资源
Map使用
假如,我们的实体,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map
接口
public int addCustomer2(Map<String,Object> map);
mapper实现
<insert id="addCustomer2" parameterType="map">
insert into customer(customer_number,name,password) values (#{id},#{name},#{password})
</insert>
测试
@Test
public void addCustomer2(){
Map<String,Object> map = new HashMap<String, Object>();
map.put("id",1000);
map.put("name","牛逼");
map.put("password","123123");
int customer2 = customerMapper.addCustomer2(map);
System.out.println(customer2);
}
区别
Map传递参数,直接在sql中取出key即可!
对象传递参数,直接在sql中取对象的属性
只有一个基本类型参数的情况下,sql可以直接取到
多个参数用Map,或者注解!
模糊查询
模糊查询
1、java代码执行的时候,传递通配符%%
List<Customer> like = customerMapper.getCustmerLike("%张%");
2、在sql中不能写的写法 %#{value}% 在sql语句中不能使用通配符
会导致sql注入使where语句失效
例where name = %#{value}%
value值为1 or 1 = 1
时会出现where子句失效。从而实现sql注入
配置解析
1、核心配置文件
mybatis-config.xml
MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
环境配置
Mybatis可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个SqlsessionFactory实例只能选择一种环境
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
学会使用配置多套运行环境
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/product_systeam?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<environment id="development">
<!--事务管理器分JDBC | MANAGED-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/product_systeam?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
Mybatis默认的事务管理器就是JDBC,连接池:POOLED
三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
属性(properties)
我们可以通过properties属性来实现引用配置文件
这些属性都是可外部配置且可动态替换的,既可以在典型的java属性文件中配置,亦可通过properties元素的子元素来传递[db.properties]
编写一个配置文件
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/product_systeam?characterEncoding=utf8&;connectTimeout=1000&;socketTimeout=3000&;autoReconnect=true&;useUnicode=true&;useSSL=false&;serverTimezone=Asia/Shanghai
username=root
password=123456
在核心配置文件中引入
<!--引入外部配置文件-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="pwd" value="123456"/>
</properties>
可直接引入外部文件
可以在其中增加一些属性配置
如果两个文件有同一个字段时,优先使用外部配置的文件
类型别名(typeAliases)
类型别名是为java类型设置一个短的名字
存在的意义仅在于用来减少类完全限定名的冗余
<!--可以给实体类写别名-->
<typeAliases>
<typeAlias type="com.wzh.demo.entity.Customer" alias="Customer"/>
</typeAliases>
- 也可以指定一个包名,Mybatis会在包名下面搜索需要的Java Bean,比如:扫描实体类的包,它的默认别名就为找个类的类名,首字母小写
<typeAliases>
<package name="com.wzh.demo"/>
</typeAliases>
在实体类比较少使用第一种
如果实体类比较多则使用第二种
但第一种自由度更高
还可以在实体类上增加注解
@Alias("customers")
public class Customer implements Serializable {
设置
网址https://mybatis.net.cn/configuration.html#settings
其他配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
mybatis-generator-core
mybatis-plus
通用mapper
映射器(mappers)
MapperRegistry: 注册绑定我们的Mapper文件
方式一:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
注意点:
- 接口和它的Mapper配置文件必须同盟
- 接口和他的Mapper配置文件必须在同一个包下
方式三:使用扫描包进行注入绑定
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
- 接口和它的Mapper配置文件必须同盟
- 接口和他的Mapper配置文件必须在同一个包下
生命周期和作用域
生命周期,和作用域,是至关重要的,因为错误的使用会导致非常严重的并发问题
SqlSessionFactoryBuilder:
- 一旦创建了SqlSessionFactoryBuilder,就不需要它了
- 局部变量
SqlSessionFactory
- 说白了就是可以想象为:数据库连接池
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- 因此SqlsessionFactory的最佳作用域是应用作用域
- 最简单的就是使用单例模式或者静态单例模式
SqlSession
- 连接到连接池的一个请求
- qlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后需要关闭,否则资源被占用
这里面每一个Mapper都代表一个具体任务
解决属性名和字段名不一致的问题
数据库中的字段
新建一个项目,拷贝之前的,测试实体类字段不一致
private String name;
/**
* 密码
*/
// private String password;
private String psw;
测试出现问题
类型处理器
select * from customer where customerNumber=#{customerNumber}
等价于
select customerNumber,name,psw,phoneNumber,customerType,sex
where customerNumber=#{customerNumber}
解决方法
起别名
select customer
customerNumber,name,password as psw ,phoneNumber,customerType,sex
where customerNumber=#{customerNumber}
结果集映射
<resultMap id="CustomerMap" type="customer">
<!--column数据库中的字段 , property实体类中的属性-->
<result column="customer_number" property="customerNumber"></result>
<result column="password" property="psw"></result>
<result column="phone_number" property="phoneNumber"></result>
<result column="customer_type" property="customerType"></result>
<result column="sex" property="sex"></result>
</resultMap>
<select id="getCustomerByNumber" parameterType="String" resultMap="CustomerMap">
select * from customer where customer_number= #{customerNumber};
</select>
resultMap元素是MyBatis中最重要最强大的元素
ResultMap的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
ResultMap最优秀的地方在于,虽然你已经对他相当了解了,但是根本就不需要显式地用到他们
日志
- 日志工厂
如果一个数据库操作,出现异常,我们需要排错,那么日志就是最好的助手
曾今:sout、debug
现在:日志工厂
- SLF4J
- LOG4J
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING
- NO_LOGGING
在Mybatis中具体使用那个一日志实现,在设置中实现
STDOUT_LOGGING标准日志输出
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
LOG4J
什么是Log4J?
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别我们能够更加细致地控制日志的生成过程。
- 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1、先导入LOG4J包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、log4j.properties
### 配置根 设置日志级别###
log4j.rootLogger = DEBUG,console,file
### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###
log4j.logger.org.apache=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
### 配置输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
Log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]%m%n
### 配置输出到文件 ###
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = logs/log.log
log4j.appender.file.MaxFileSize = 10mb
log4j.appender.file.Append = true
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
3.配置log4j为日志的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4、Log4j的使用,直接测试运行刚才查询
简单使用
在要使用Log4j的类中,导入包 import org.apache.log4j.Logger
日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(CustomerTest.class);
日志级别
logger.info("info:进入了testLog4j");
logger.debug("info:进入了testLog4j");
logger.error("info:进入了testLog4j");
分页
为什么要分页?
- 减少数据处理量
使用Limit分页
select *
from customer
limit 开始的位置,页的大小;
select *
from customer
limit 2; #[0,n]
接口
List<Customer> getCustomerByLimit(Map<String,Integer> map);
实现mapper
<select id="getCustomerByLimit" parameterType="map" resultType="customer">
select * from customer limit #{startIndex},#{pageSize}
</select>
测试
@Test
public void getCustomerByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
Map<String,Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<Customer> customerByLimit = mapper.getCustomerByLimit(map);
for (Customer customer : customerByLimit) {
System.out.println(customer);
}
sqlSession.close();
}
RowBounds分页
分页插件PageHelper
使用注解开发
面向接口编程
使用注解开发
1、注解在接口上实现
@Select("select * from return_factory")
public List<ReturnFactory> findAll();
需要在核心配置文件中绑定接口
<mappers>
<!-- <mapper resource="com/wzh/demo/mapper/CustomerMapper.xml"/>-->
<mapper class="com.wzh.demo.mapper.CustomerMapper"></mapper>
<!-- <package name="com.wzh.demo"/>-->
</mappers>
crud
配置工具类实现自动提交
public static SqlSession getSqlSession(){
SqlSession sqlSession = sqlSessionFactory.openSession(true);
return sqlSession;
}
接口
@Select("select * from customer where customer_number=#{id}")
public Customer getCustomerByID(@Param("id") String customerNumber);
/*注解开发插入*/
@Insert("insert into customer(customer_number,name,password) values(#{customerNumber},#{name},#{psw}) ")
public int insertCustomerzhujie(Customer customer);
@Update("update customer set name = #{name},password = #{psw} where customer_number = #{customerNumber}")
int updateCustomer(Customer customer);
@Delete("delete from customer where customer_number = #{psw}")
int deleteCustomerBynumber(String id);
必须将注册接口绑定到配置文件中
@Param()
基本类型从参数和String类型需要加上
引用类型不需要加
如果只有一个基本类型,可以忽略,建议加上
在sql中引用的就是这里的定义值