在学习SpringBoot时想连接数据库做一些操作,从网上找了一些博客,总感觉很多解释的不清楚。自己写一篇总结,主要是本人使用中踩过的坑。
1.准备两张带有级联关系的数据表User表和Department表,先创建Department表,在User表中维护关联关系,并向Department表中添加数据。
CREATE TABLE `department` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; INSERT INTO `department` VALUES (1, '研发部'); INSERT INTO `department` VALUES (2, '销售部'); INSERT INTO `department` VALUES (3, '测试部'); INSERT INTO `department` VALUES (4, '商品部'); INSERT INTO `department` VALUES (5, '采购部'); CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) NOT NULL, `password` varchar(45) NOT NULL, `identify_type` tinyint(4) NOT NULL, `identify_number` varchar(45) NOT NULL, `dept_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fk_user_dept` (`dept_id`), CONSTRAINT `fk_user_dept` FOREIGN KEY (`dept_id`) REFERENCES `department` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
2.添加Maven依赖,只添加这块需要用到的依赖,MySQL驱动,MyBatis,Lombok,以及用于测试的test,其它依赖项没有展示。
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency>
3.创建POJO对象。因添加有lombok,使用@Data注解,可以代替getter/setter方法。生成一个无参构造器和全参构造器。
package com.cn.entities.mysql; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Department { private Integer id; private String name; }
package com.cn.entities.mysql; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class User { private Integer id; private String name; private String password; private Integer identifyType; private String identifyNumber; private Department department; }
4.在application.properties中配置MySQL和MyBatis。
spring.datasource.url=jdbc:mysql://localhost/test?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver mybatis.type-aliases-package=com.cn.entities.mysql mybatis.mapper-locations=classpath:mapper/*.xml server.port=8081
需要注意的有以下几点
- MySQL驱动版本不同,driver-class-name使用com.mysql.cj.jdbc.Driver而不是原来的com.mysql.jdbc.Driver。使用原来的也不会报错,但是会输出一个提示。所以我做了修改。
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'.
The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
- 需要添加useSSL=false&serverTimezone=UTC。不使用SSL,并设置时区。若不设置时区,会出现以下错误。
com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
- MyBatis的type-aliases-package我设置的是POJO取别名。
5.创建Dao,UserMapper.java。
package com.cn.dao.mysql; import com.cn.entities.mysql.User; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; @Mapper @Component public interface UserMapper { User fetchById(Object id); List<User> fetchList(Map<String, Object> query); int save(User user); int deleteById(Object id); int update(User user); }
6.在resources资源文件目录下创建mapper文件夹并创建UserMapper.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="com.cn.dao.mysql.UserMapper"> <!--做返回结果映射,添加了resultMap之后,在select中就可以将resultMap指定为userResultMap,结果会映射为对象--> <resultMap id="userResultMap" type="User"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="password" column="password"/> <result property="identifyType" column="identify_type"/> <result property="identifyNumber" column="identify_number"/> <!--关联Department表,javaType中指定Department的类路径,property中是对象的属性--> <association property="department" javaType="com.cn.entities.mysql.Department"> <!--此处的id指Department对象中的属性,dept_id是user表中的dept_id,关联字段--> <id property="id" column="dept_id"/> <result property="name" column="dept_name"/> </association> </resultMap> <sql id="select_statement"> select u.id, u.name, u.password, u.identify_number, u.identify_type, u.dept_id, t.id, t.name dept_name from user u left join department t on u.dept_id = t.id where 1 = 1 </sql> <select id="fetchById" parameterType="java.lang.Object" resultMap="userResultMap"> <include refid="select_statement"/> and t.id = #{id} </select> <select id="fetchList" parameterType="java.util.HashMap" resultMap="userResultMap"> <include refid="select_statement"/> </select> <insert id="save" parameterType="User"> insert into user(name,password,identify_type,identify_number,dept_id) values(#{name},#{password},#{identifyType},#{identifyNumber},#{department.id}) </insert> <update id="update" parameterType="User"> update user <trim prefix="set" suffix="where id = #{id}" suffixOverrides=","> <if test="name != null">name=#{name},</if> <if test="password != null">password=#{password},</if> <if test="identifyType != null">identify_type=#{identifyType},</if> <if test="identifyNumber != null">identify_number=#{identifyNumber},</if> <if test="department.id != null">dept_id=#{department.id},</if> </trim> </update> <delete id="deleteById" parameterType="java.lang.Object"> delete from user where id = #{id}; </delete> </mapper>
一句话总结,resultMap中property映射的是对象的属性,column映射的是sql语句中的列,若需要做对应,则需指定对应的列别名
6.创建service。IBaseService.java,BaseServiceImpl.java,IUserService.java,UserServiceImpl.java
package com.cn.service; import java.util.List; import java.util.Map; public interface IBaseService<T> { public abstract T fetchById(Object id); public abstract List<T> fetchList(Map<String, Object> query); public abstract boolean save(T t); public abstract boolean update(T t); public abstract boolean deleteById(Object id); }
package com.cn.service.impl; import com.cn.service.IBaseService; import java.util.List; import java.util.Map; public class BaseServiceImpl<T> implements IBaseService<T> { @Override public T fetchById(Object id) { return null; } @Override public boolean save(T t) { return false; } @Override public boolean deleteById(Object id) { return false; } @Override public List<T> fetchList(Map query) { return null; } @Override public boolean update(T t) { return false; } }
package com.cn.service; import com.cn.entities.mysql.User; public interface IUserService extends IBaseService<User>{ }
package com.cn.service.impl; import com.cn.dao.mysql.UserMapper; import com.cn.entities.mysql.User; import com.cn.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; @Service public class UserServiceImpl extends BaseServiceImpl<User> implements IUserService { @Autowired private UserMapper userMapper; @Override public User fetchById(Object id) { return userMapper.fetchById(id); } @Override public boolean save(User user) { try { userMapper.save(user); return true; } catch (Exception e) { e.printStackTrace(); } return false; } @Override public boolean deleteById(Object id) { try { userMapper.deleteById(id); return true; } catch (Exception e) { e.printStackTrace(); } return false; } @Override public List<User> fetchList(Map query) { return userMapper.fetchList(query); } @Override public boolean update(User user) { try { userMapper.update(user); return true; } catch (Exception e) { e.printStackTrace(); } return false; } }
7.启用MapperScan扫描dao,com.cn.dao.mysql为dao存放的包路径。
package com.cn; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @MapperScan("com.cn.dao.mysql") @EnableScheduling public class TissueDaoApplication { public static void main(String[] args) { SpringApplication.run(TissueDaoApplication.class, args); } }
8.进行测试
src/test/java/com/cn/service目录下创建TestUserService.java进行测试。
package com.cn.service; import com.cn.entities.mysql.Department; import com.cn.entities.mysql.User; import com.cn.util.Json; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class TestUserService { @Autowired private IUserService userService; @Test public void create(){ User user = new User(); // 模拟数据库查询部门信息 Department department = new Department(); department.setId(4); department.setName("商品部"); user.setDepartment(department); user.setName("张三"); user.setPassword("123456"); user.setIdentifyType(0); user.setIdentifyNumber("420612345678910001"); userService.save(user); } @Test public void update(){ User user = userService.fetchById(2); System.out.println(Json.toJson(user)); // 模拟数据库查询部门信息 Department department = new Department(); department.setId(4); department.setName("商品部"); user.setDepartment(department); System.out.println(Json.toJson(user)); userService.update(user); } @Test public void fetchById(){ System.out.println(Json.toJson(userService.fetchById(1))); } }
另外需要注意的是,个人建议在查询时最好是将所有的列展示出来,而不要用select *。在级联情况下具有同名字段时,最好另取别名。在查询条件中如果条件是级联表中同名字段,需要指定具体查询哪张表。