项目搭建
新建项目Spring Initializr
Spring Initializr项目配置
指定Java版本为8
选择依赖
选择启动服务url除了官网之外,可选择https://start.aliyun.com/
项目名称和位置
自定义项目名称和位置
点击确定,项目构建成功
依赖jar包管理
在pom.xml文件中找到<dependencies>标签,该标签包裹的就是当前所有依赖对应的jar包
可在标签内添加想要的依赖,例如,添加单元测试和mybatis所需要的依赖
方法一:输入junit,自动弹出提示,回车
方法二:访问https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.12,在搜索栏中直接搜索
选择第一个搜索结果,选择对应的版本,(以3.4.6版本为例)
复制内容,粘贴到pom.xml文件中的<dependencies>标签内
一些问题的解决办法
pom.xml文件version报错
解决办法一:版本号过高,降低版本号(一般会自动匹配jdk生成对应版版本号)
解决办法二:在IDEA中的Maven中配置错误
点击文件->设置
在构建、执行、部署工具->Maven中更改maven主路径
搭建MyBatis开发环境
添加依赖
在pom.xml中添加mybatis和mysql相关依赖
核心配置文件
添加mybatis-config.xml核心配置文件
在src/main/resource下新建添加mybatis-config.xml
在mybatis-config.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">
添加db资源包
在src/main/resource下新建->资源包,指定包名为db
在新建的db资源包内写入mysql数据库链接信息
下面的代码有五行(包括注释)每行后面不能有空格,每个字母之间也不能有空格
#mysql数据库链接信息 driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC username=root password=root
在mybatis-config.xml文件<configuration>标签中添加链接数据库相关代码
<!-- 读取外部的数据源配置信息--> <properties resource="db.properties"></properties> <!-- 指定类别名所在的包路径--> <typeAliases> <package name="com.example.demo"/> </typeAliases> <environments default="demo"> <environment id="demo"> <!-- 使用JDBC的事务管理器--> <transactionManager type="JDBC"></transactionManager> <!-- 以连接池方式连接mydql数据库--> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
添加SQL映射文件
在src/main/resources下新建一个mapper目录,在该目录下新建GradeMapper.xml文件
在GradeMapper.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>标签,并继续往<mapper>标签内添加数据库操作相关语句,例如
<!--com.example.demo.Dept.queryCount--> <mapper namespace="com.example.demo.Dept"><!-- 名字空间自己随便,一般指定所用的类的位置 --> <select id="queryCount" resultType="Long"> select count(1) from tb_dept </select>
</mapper>
在mybatis-config.xml的dependencies标签下添加mapper文件的映射
<!-- mapper映射文件 --> <mappers> <mapper resource="mapper/GradeMapper.xml"></mapper> </mappers>
添加实体类
新建一个java类用作数据库实体映射的实体类,添加属性和get/set以及toString方法,例如:
构建myBatis的核心对象
新建一个MybatisTest用来测试
public class MybatisTest { public static void main(String[] args) { InputStream inputStream = null; SqlSessionFactoryBuilder builder = null; SqlSessionFactory sessionFactory = null; SqlSession session = null; try { // 读取mybatis-config.xml文件 inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 创建工厂构建器 builder = new SqlSessionFactoryBuilder(); // 创建会话工厂 sessionFactory = builder.build(inputStream); // 创建会话 session = sessionFactory.openSession(); // 通过映射文件中的namespace.id的形式找到相对应的方法 //此处添加查询、插入、更改、删除等数据库操作代码 } catch (Exception e) { session.rollback();//插入、删除等commit()修改数据库的操作要写回滚函数 e.printStackTrace(); } finally { // 关闭会话 session.close(); } } }
注意:此时导入的方法必须都是与mybatis有关的
单元测试
添加依赖
在pom.xml的<dependencies>标签中添加Junit相关依赖
<!-- junit单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> </dependency>
添加测试类
在src/test目录下添加要测试的类的同名+Test新建类,如:关于Class的测试ClassTest
并在要测试的方法前使用@Test
在测试类中,添加要构造函数,例如:Example example=new Example();
public class ExampleTest { Example example=new Example(); @Test public void add(){ int s=example.add(10,20); System.out.println("s="+s); } @Test public void sum(){ int sum =example.sum(); System.out.println("sum="+sum); } }
数据库的简单操作
不带参数的单表查询
在MybatisTest类Main中,返回一条数据用selectOne()查询,Long型变量接收查询结果,例如:
// 通过映射文件中的namespace.id的形式找到相对应的方法 Long count = session.selectOne("abc.queryCount");
返回多条数据用selectList()查询,用List<Object>型变量接收查询结果,例如:
List<Emp> list = session.selectList("emp.dao.queryAllEmp"); for (Emp emp : list) { System.out.println(emp.toString()); }
在select()系列函数中第一个参数是所在的页面通过映射文件中的namespace.id的形式,例如:
emp.dao.queryAllEmp,对应src/main/resources/mapper/EmpMapper.xml里的namespace和id,在<select>标签内写数据库查询语句:
<mapper namespace="emp.dao"> <!-- 查询表中总记录数 --> <select id="queryAllEmp" resultType="Emp"> select * from tb_emp </select> </mapper>
其中的返回的类型resultType则对应MybatisTest类main()中的接收参数的List<Object>类型的Object内的内容,例如List<Emp>
带参数的单表查询
selectOne()和selectList有两个参数,
第二个参数:可以传递的参数。
要传递单个参数直接填入,多个参数则需要将其写成一个对象或者使用hashMap
//多参数传递 Dept dept1 = new Dept(); dept1.setDeptNo(1003); dept1.setDeptName("测试部"); List<Dept> list1 = session.selectList("abc.queryDept", dept1); // map中key必须和映射文件中的占位符名称完全一样 HashMap<String, Object> map = new HashMap<String, Object>(); map.put("deptName","测试部"); map.put("deptNo",1003); List<Dept> list2 = session.selectList("abc.queryDept", map); for (Dept dept : list1) { System.out.println(dept.toString()); }
在对应的映射文件src/main/resources/mapper/GradeMapper.xml中<select>标签内添加元素:parameterType,它的值对应select系列函数的第二参数的类型
//<select id="queryDept" resultType="Dept" parameterType="Dept"> <select id="queryDept" resultType="Dept" parameterType="Map"> select * from tb_dept d where d.deptNo=#{deptNo} and d.deptName=#{deptName} </select>
多参数的插入
在MybatisTest类的Main中,用insert()函数写数据库插入代码,多参数传递使用Object或则HashMap类型(案例参照带参数的单表查询):
Dept dept = new Dept(); dept.setDeptNo(1004); dept.setName("研发部") session.insert("com.example.demo.Dept.addDept",dept); session.commit(); System.out.println("操作成功");
在对应的映射文件src/main/resources/mapper/GradeMapper.xml中<insert>标签内添加元素:parameterType,它的值对应insert函数的第二参数的类型
<insert id="addDept" parameterType="Dept"> insert into tb_dept value (#{deptNo},#{deptName},#{remark}) </insert>
修改和删除
在MybatisTest类的Main中,用update()函数写数据库修改和删除代码:
// 修改 Dept dept = new Dept(); dept.setDeptNo(1004); dept.setName("研发部") session.update("com.example.demo.Dept.updateDept",dept); session.commit(); System.out.println("操作成功"); // 删除 session.update("com.example.demo.Dept.delDept",1004); System.out.println("操作成功");
在对应的映射文件src/main/resources/mapper/GradeMapper.xml中<update>或<delete>标签内添加元素:parameterType,它的值对应update函数的第二参数的类型
<update id="updateDept" parameterType="Dept"> update tb_dept d set d.deptName=#{deptName},remark=#{remark} where d.deptNo=#{deptNo} </update>
<delete id="delDept" parameterType="int"> delete from tb_dept where deptNo=#{deptNo} </delete>
多表查询
涉及到多个表的查询要对实体类进行修改,例如以tb_dept部门表作为主表,tb_emp员工表的deptNo作为外键,需要在Dept类中添加集合属性:
public class Dept implements Serializable { private Integer deptNo; private String deptName; private String deptRemark; private List<Emp> emps = new ArrayList<Emp>(); // 集合属性:多个员工 }
然后更新get/set方法和toString方法;
在Emp类中添加映射对象属性:
public class Emp implements Serializable { private Integer empNo; private String empName; private Double salary; private Date hireDate; private Dept dept; // 映射对象属性 }
并更新get/set 方法,但是toString()方法不需要重写。
在MybatisTest类的Main中写入查询相关代码
// 查询特定员工所在的部门 List<Emp> list2 = session.selectList("emp.dao.queryDeptByEmp"); for (Emp emp : list2) { System.out.println(emp.getEmpName() + "/t" + emp.getDept().getDeptName());} session.commit();
// 根据部门查询员工 List<Dept> list1 = session.selectList("abc.queryEmpByDept"); for (Dept dept : list1) { System.out.println(dept.toString()); List<Emp> emps = dept.getEmps(); for (Emp emp : emps) { System.out.println(emp.toString()); } } session.commit(); System.out.println("==========操作成功");
在对应的映射文件src/main/resources/mapper/GradeMapper.xml中写入二次映射实体对象
<!-- 二次映射实体对象Dept --> <resultMap id="deptMap" type="Dept"> <id property="deptNo" column="deptNo"></id> <result property="deptName" column="deptName"></result> <result property="deptRemark" column="remark"></result> <!-- 集合属性的映射--> <collection property="emps" ofType="Emp" > <id property="empNo" column="empNo"></id> <result property="empName" column="empName"></result> <result property="salary" column="salary"></result> <result property="hireDate" column="hireDate"></result> </collection> </resultMap>
接着写入select查询语句结果返回值为二次映射对象id
<!-- 查询特定员工所在的部门--> <select id="queryDeptByEmp" resultMap="empMap"> SELECT e.empName,d.deptName FROM tb_dept d,tb_emp e WHERE d.deptNo = e.deptNo AND e.empName = '李四' </select>
在另一表对应的映射文件src/main/resources/mapper/EmpMapper.xml也写入二次映射实体对象:
<resultMap id="empMap" type="Emp"> <id property="empNo" column="empNo"></id> <result property="empName" column="empName"></result> <result property="salary" column="salary"></result> <result property="hireDate" column="hireDate"></result> <!-- 映射对象属性 --> <association property="dept" column="deptNo" javaType="Dept"> <id property="deptNo" column="deptNo"></id> <result property="deptName" column="deptName"></result> <result property="deptRemark" column="remark"></result> </association> </resultMap>
<select id="queryEmpByDept" resultMap="deptMap"> SELECT e.empName, e.hireDate, d.deptName FROM tb_dept d, tb_emp e WHERE d.deptNo = e.deptNo AND d.deptName = '人事部' </select> <select id="queryDept" resultMap="deptMap"> select * from tb_dept d </select>
接着写入select查询语句结果返回值为二次映射对象id
至此需要实现的内容代码已经基本书写完成,接下来是代码优化,通过观察对比两个表的二次映射对象不难发现,其中有两端的代码重复,如tb_dept表的映射对象属性在tb_emp表的二次映射对象中重复tb_emp表也有相似的代码重复。
由于这两个映射文件都在src/main/resources/mybatis-config.xml中加在了
所以,这两映射文件可以添加resultMap={“”}属性相互引入,即
动态条件查询
<if>和<where>示例:
<if>和<set>示例:
<trim>和<if>示例:
子查询
子查询示例:
在src/main/java/com/example/mybatisdemo/test/MybatisTest.java类的Main中
Integer[] ids={1001,1002}; List<Dept> list =session.selectList("abc.subQueryDept",ids); for (Dept d :list){ System.out.println(d.toString()); }
在对应的映射文件src/main/resources/mapper/GradeMapper.xml中:
<select id="subQueryDept" resultMap="deptMap"> //select * from tb_dept d where d.deptNo in(1001,1002) select * from tb_dept d where d.deptNo in <foreach collection="array" item="ids" open="(" close=")" separator=","> #{ids} </foreach> </select>
其中除了用数组的形式,还可以用hashMap来写MybatisTest.java类中的查询代码:
//子查询 ArrayList<Integer> arys = new ArrayList<>(); arys.add(1001); arys.add(1002); HashMap<String, List> ids = new HashMap<>(); ids.put("array",arys); // Integer[] ids={1001,1002}; List<Dept> list =session.selectList("abc.subQueryDept",ids); for (Dept d :list){ System.out.println(d.toString()); }
分页查询
分页查询示例:
在src/main/java/com/example/mybatisdemo/test/MybatisTest.java类的Main中
// 分页查询 HashMap<String, Object> map = new HashMap<>(); map.put("index",0); map.put("pageSize",2); List<Dept> list = session.selectList("abc.queryDeptByPage",map); for (Dept d : list){ System.out.println(d.toString()); }
在对应的映射文件src/main/resources/mapper/GradeMapper.xml中:
<select id="queryDeptByPage" resultMap="deptMap" parameterType="map"> select * from tb_dept limit #{index},#{pageSize} </select>
模糊查询
方法1(通用):在src/main/java/com/example/mybatisdemo/test/MybatisTest类Main中
//模糊查询 Dept dept2=new Dept(); dept2.setDeptName("%"+"财"+"%"); List<Dept> fuzzyDeptNameList = session.selectList("deptSpace.fuzzyQueryDeptName", dept2); System.out.println("模糊查询"); for (Dept d : fuzzyDeptNameList){ System.out.println(d.toString()); }
在对应的映射文件src/main/resources/mapper/GradeMapper.xml中:
<!-- 方法2:仅限mysql使用concat--> <!--select * from tb_dept where deptName like CONCAT('%','人','%');--> <select id="fuzzyQueryDeptName" resultMap="deptMap"> select * from tb_dept where deptName like #{deptName}</select>
使用接口调用
新建接口
新建一个包用来存储接口类,新建接口类(例如:DeptDao)
添加操作数据库方法
将对应的数据库映射文件中的操作数据库语句的id写成函数名写入接口类中(例如:GradeMapper.xml中的数据库语句写入DeptDao)
/*** * 部门管理数据访问接口 */ public interface DeptDao { public Long queryCount(); public List<Dept> queryDept(Dept dept); /** //部门的查询 Long deptCount = session.selectOne("deptSpace.queryCount"); List<Dept> deptList = session.selectList("deptSpace.queryDept", dept); */ }
更改映射文件
更改映射文件的namespace为接口类的路径(例如:DeptDao和GradeMapper)
<!-- 在映射文件GradeMapper中--> <!--<mapper namespace="deptSpace">--> <mapper namespace="org.example.dao.DeptDao">
更改关联表的映射文件中引用的该映射文件的namespce(例如:GradeMapper和EmpMapper)
<!--在关联表的映射文件EmpMapper中--> <!-- 集合属性映射--> <association property="dept" column="deptNo" javaType="Dept" resultMap="org.example.dao.DeptDao.deptMap"></association>
使用接口调用
在src/main/java/com/example/mybatisdemo/test/MybatisTest类Main中(例如:DeptDao)
//使用接口调用 Dept dept=new Dept(); dept.setDeptNo(1001); dept.setDeptName("财务部"); dept.setRemark(""); DeptDao deptDao = session.getMapper(DeptDao.class); List<Dept> depts = deptDao.queryDept(dept); System.out.println("接口的调用"); for (Dept d : depts){ System.out.println(d.toString()); }
整合Spring Boot
配置文件
在src/main/resources/application.yml,链接mysql数据库
#链接mysql数据库 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3307/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC username: root password: 123456
Pojo实体函数层
在java的主目录下新建po目录,并添加需要的数据库实体类(例如:Dept和Emp)
和在resource下添加对应的数据库映射文件
Dao(DAL)数据访问层
在java的主目录下新建dao目录,并添加po目录下实体类对应的数据访问接口,
/*** * 部门管理数据访问接口 * 加注解 */ @Mapper public interface DeptDao { public Long queryCount(); public List<Dept> queryEmpByDept(); public List<Dept> queryDept(Dept dept); }
例如DeptDao:
在src/main/resources/application.yml整合mybatis
#整合mybatis mybatis: #加载实体类所在的包类 type-aliases-package: springbootdemo.demo.po #加载映射文件 mapper-locations: mapper/*.xml #把日志输出到控制台 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
简单地测试数据访问
在src/test/java/springbootdemo/demo/DemoApplicationTests.java添加测试代码:
//注入mapper @Autowired private DeptDao deptDao; @Test public void testQueryDept(){ List<Dept> list = this.deptDao.queryDept(new Dept()); System.out.println("测试成功"); for (Dept d : list){ System.out.println(d.toString()); } }
点击测试,出结果测试成功
Service(BLL)业务逻辑层
在java的主目录下新建service目录,并添加po目录下实体类对应的业务逻辑接口和业务逻辑实现类
/*** * 部门业务逻辑接口 */ public interface DeptService { public Long queryCount(); public List<Dept> queryEmpByDept(); public List<Dept> queryDept(Dept dept); }
/*** * 部门业务逻辑实现 */ @Service @Transactional public class DeptServiceImpl implements DeptService{ //注入 @Autowired private DeptDao deptDao; //实现方法 //数据库的事务只有增删改,查不参与(特殊处理):Transactional @Override // @Transactional(Propagation.SUPPORTS) @Transactional(readOnly = true) public Long queryCount() { return this.deptDao.queryCount(); } @Override @Transactional(readOnly = true) public List<Dept> queryEmpByDept() { return this.deptDao.queryEmpByDept(); } @Override @Transactional(readOnly = true) public List<Dept> queryDept(Dept dept) { return this.deptDao.queryDept(dept); } }
Controller(UI)界面控制层(同步)
在java的主目录下新建controller目录,并添加po目录下实体类对应的界面控制类
/** * 同步,客户端和服务器端, * 页面提交请求,整个页面加载完才能做第二次操作, * 整个页面的完整刷新,要有等待 * */ @Controller //异步,页面提交请求,服务器没有响应,依然可以二次操作,局部刷新 //@RestController public class DeptController { //注入 @Autowired private DeptService deptService; //识别方法 @RequestMapping("/index") //RequestMapping的细化 // @GetMapping("/index") // @PostMapping("/index") public String index(Model model){ List<Dept> list = this.deptService.queryDept(new Dept()); System.out.println("查询成功"); for (Dept d : list){ System.out.println(d.toString()); } model.addAttribute("list",list); //返回页面的名称(返回的字符串必须和页面的名称相同) return "index";//index.html默认已经添加后缀 } @RequestMapping("/query") public String query(){ return "list";//错误示例 } }
在src/main/resources/templates中对应的 HTML文件(例如index.html和list.html)
并在其<html>标签中添加 xmlns:th=”http://www.thymeleaf.org”建立映射
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <table> <tr> <th>编号</th> <th>名称</th> <th>备注</th> </tr> <tr th:each="dept:${list}"> <td th:text="${dept.deptNo}"></td> <td th:text="${dept.deptName}"></td> <td th:text="${dept.remark}"></td>
<td>
<a th:href="@{/del(deptNo=${dept.deptNo})}">删除</a>
<a th:href='@{/gotoEdit/}+${dept.deptNo}'>修改</a>
</td> </tr> </table> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title>list</title> </head> <body> <p>列表</p> </body> </html>
最后在java主函数中添加注解
//开启事务管理
@EnableTransactionManagemen
Controller(UI)界面控制层(异步)
//异步调用 @RequestMapping("/ajaxQuery") @ResponseBody @ApiOperation("异步查询部门") public List<Dept> ajaxQuery(){ List<Dept> list = this.deptService.queryDept(new Dept()); return list; } @RequestMapping("/ajaxQueryAll") @ResponseBody @ApiOperation("异步查询员工") public List<Dept> ajaxQueryAll(){ List<Dept> list = this.deptService.queryAllDept(new Dept()); return list; } @RequestMapping("/ajaxSaveDept") @ResponseBody @ApiOperation("异步保存添加部门") public String ajaxSaveDept(@RequestBody Dept dept){ this.deptService.addDept(dept); return "success"; } @RequestMapping("/ajaxDelDept/{deptNo}")//开启事务管理 @ResponseBody @ApiOperation("异步删除部门") public String ajaxDelDept(@PathVariable String deptNo){ this.deptService.delDept(Integer.valueOf(deptNo)); return "success"; } @GetMapping("/ajaxQueryDept/{deptNo}") @ResponseBody @ApiOperation("异步根据编码查询部门") public Dept ajaxQueryDept(@PathVariable String deptNo){ Dept dept = new Dept(); dept.setDeptNo(Integer.valueOf(deptNo)); List<Dept> list = this.deptService.queryDept(dept); return list.get(0); } @PostMapping("/ajaxUpdateDept") @ResponseBody @ApiOperation("异步删除部门") public String ajaxUpdateDept(@RequestBody Dept dept){ this.deptService.updateDept(dept); return "success"; }
在src/config 新建一个CorsFilter类,用作跨域处理
import org.springframework.context.annotation.Configuration; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 跨域过滤器,解决前后端分离时前端项目的跨域请求报错 */ @Configuration public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; // 不使用*,自动适配跨域域名,避免携带Cookie时失效 String origin = request.getHeader("Origin"); if (origin != null && !origin.equals("")) { response.setHeader("Access-Control-Allow-Origin", origin); } // 自适应所有自定义头 String headers = request.getHeader("Access-Control-Request-Headers"); if (headers != null && !headers.equals("")) { response.setHeader("Access-Control-Allow-Headers", headers); response.setHeader("Access-Control-Expose-Headers", headers); } // 允许跨域的请求方法类型 response.setHeader("Access-Control-Allow-Methods", "*"); // 预检命令(OPTIONS)缓存时间,单位:秒 response.setHeader("Access-Control-Max-Age", "3600"); // 明确许可客户端发送Cookie,不允许删除字段即可 response.setHeader("Access-Control-Allow-Credentials", "true"); chain.doFilter(request, response); } }
配置application文件,更改服务器端口
#链接mysql数据库 #com.mysql.jdbc.Driver高版本数据库链接要添加cj:com.mysql.cj.jdbc.Driver spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: 123456 mvc: format: date: yyyy-MM-dd jackson: date-format: yyyy-MM-dd time-zone: Asia/Shanghai #整合mybatis#GMT mybatis: #加载实体类所在的包类 type-aliases-package: springbootdemo.demo.po #加载映射文件 mapper-locations: mapper/*.xml #把日志输出到控制台 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl server: port: 8888
未完待续。。。
原创文章,作者:端木书台,如若转载,请注明出处:https://blog.ytso.com/267418.html