在學習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 *。在級聯情況下具有同名字段時,最好另取別名。在查詢條件中如果條件是級聯表中同名字段,需要指定具體查詢哪張表。