版權聲明:本文爲博主原創文章,未經博主容許不得轉載。java
本文的出發點在於:本人師傅比較NB,在項目中數據庫操做用的MyBatis,須要涉及到多對多的操做,問師傅多對可能是否須要創建關聯實體,師傅說了句「低級的能夠建,高級的不須要」,因而乎,爲了挑戰難度,在項目中最終以不創建關聯實體解決了多對多的操做問題。因而空閒的時間本身用
spring-mvc 4
、MyBatis
、MySQL
、Maven
從零搭建起一個 簡單的demo,一是爲了從頭至尾鞏固理解,二是爲了留着之後忘記了能夠查閱,三是也但願能幫助到一些像我這樣菜鳥級別的人。此片文章可能很是長,我寫的步驟也算很細了,NB的人確定認爲很累贅,不太重在分享!web
- 操做系統:Windows 7
- 開發工具:Intellij IDEA 14
- 數據庫鏈接:Navicat 11 Premium
- 接口測試:Postman
- 對學生student的操做:學生學號必須惟一
- 添加學生信息;
- 修改學生信息;
- 按學號查詢學生信息;
- 查詢全部學生信息(不加載所選教師信息);
- 查詢全部學生信息(同時加載所選教師信息);
- 對教師teacher的操做:教師工號必須惟一
- 添加教師信息;
- 修改教師信息;
- 按工號查詢教師信息;
- 查詢全部教師信息(不加載所教學生信息);
- 查詢全部教師信息(同時加載所教學生信息);
- 對關聯表student_teacher的操做:學生選授課教師
- 學生選擇多個授課教師,關聯信息插入關聯表中
- junit-3.8.1.jar:用於單元測試;
- spring-jdbc-4.1.6.RELEASE.jar:事務管理;
- spring-webmvc-4.1.6.RELEASE.jar:spring-mvc 4必須;
- spring-aspects-4.1.6.RELEASE.jar:面向切面編程;
- mybatis-3.2.8.jar:MyBatis必須;
- mybatis-spring-1.2.2.jar:MyBatis整合spring所需;
- commons-dbcp-1.4.jar:數據庫鏈接池;
- slf4j-log4j12-1.7.7.jar:打印日誌;
- servlet-api-2.5.jar:servlet容器所需
- fastjson-1.1.36.jar:json數據轉換工具;
- jackson-databind-2.3.3.jar:序列化類爲json格式數據所需;
- mysql-connector-java-5.1.32.jar:java鏈接MySQL數據庫驅動包
1.ER圖:
spring2.student表:
sql3.teacher表:
數據庫4.關聯表student_teacher:
express5.關聯表增長外鍵與student和teacher關聯:
apache
1.新建maven webapp工程:
編程2.填寫maven座標:
json3.新建完成的工程:
4.配置maven:
(1).maven home directory:maven解壓後所在的路徑;
(2).usere settings file:maven conf目錄下的setting文件,具體配置略;
(3).local repository:本地maven倉庫所在的目錄,用於存放jar
新建entity包、dao包、service包、impl包、controller包,結構按需自定義
修改項目編碼:三處均爲 「UTF-8」
添加項目所需的全部jar的maven座標:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.1.6.RELEASE</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.1.36</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> <scope>runtime</scope> </dependency> </dependencies>
#mysql database setting jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1/mybatis-demo?useUnicode=true&characterEncoding=utf-8 jdbc.username=user jdbc.password=123456 #connection pool settings jdbc.pool.minIdle=3 jdbc.pool.maxIdle=5 jdbc.pool.maxActive=15 jdbc.pool.maxWait=120000
2 . applicationContext.xml:spring容器核心配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" default-lazy-init="true"> <description>Spring公共配置</description> <!-- 該 BeanPostProcessor 將自動對標註 @Autowired 的 Bean 進行注入 --> <context:annotation-config/> <!-- 使用annotation 自動註冊bean, 並保證@Required、@Autowired的屬性被注入 --> <context:component-scan base-package="com.xiaolong.demo"/> <!-- MyBatis配置 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 自動掃描entity目錄, 省掉Configuration.xml裏的手工配置 --> <property name="typeAliasesPackage" value="com.xiaolong.demo"/> <!-- 顯式指定Mapper文件位置 --> <property name="mapperLocations" value="classpath:/mybatis/*Mapper.xml"/> </bean> <!-- 掃描basePackage下全部以@MyBatisRepository標識的 接口 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.xiaolong.demo.dao"/> <property name="annotationClass" value="com.xiaolong.demo.dao.MyBatisRepository"/> </bean> <!-- 使用annotation定義事務 --> <tx:annotation-driven proxy-target-class="true"/> <bean id="transactionManager" name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <!--<tx:attributes>--> <!--<tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/>--> <!--</tx:attributes>--> </tx:advice> <aop:config proxy-target-class="true"> <aop:pointcut id="transactionPointcut" expression="execution(* com.xiaolong.demo.service.impl.*.*(..))"/> <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice"/> </aop:config> <!-- local development環境 --> <beans profile="development"> <context:property-placeholder ignore-resource-not-found="true" location="classpath*:/application.development.properties"/> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="maxActive"> <value>${jdbc.pool.maxActive}</value> </property> <property name="maxIdle"> <value>${jdbc.pool.maxIdle}</value> </property> <property name="minIdle"> <value>${jdbc.pool.minIdle}</value> </property> <property name="maxWait"> <value>${jdbc.pool.maxWait}</value> </property> </bean> <bean id="studentService" class="com.xiaolong.demo.service.impl.StudentServiceImpl" /> <bean id="teacherService" class="com.xiaolong.demo.service.impl.TeacherServiceImpl" /> </beans> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> <!-- 自動掃描且只掃描@Controller --> <context:component-scan base-package="com.xiaolong.demo" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> </context:component-scan> <!--<context:property-placeholder location="/WEB-INF/*.properties"/>--> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <!-- 將StringHttpMessageConverter的默認編碼設爲UTF-8 --> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> </bean> </mvc:message-converters> </mvc:annotation-driven> </beans>
Student實體:
public class Student { private Integer id; //id private String name; //姓名 private String number; //學號,保證惟一 private String gender; //性別 private Integer age; //年齡 private List<Teacher> teachers; //授課老師集合 ..........省略get、set............ }
Teacher實體:
public class Teacher { private Integer id; //id private String name; //姓名 private String number; //工號,保證惟一 private String gender; //性別 private Integer age; //年齡 private List<Student> students; //所教學生集合 ............省略get、set方法..................... }
在此以前須要先寫個註解類MyBatisRepository
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface MyBatisRepository { }
而後新建StudentMyBatisDao接口
@MyBatisRepository public interface StudentMyBatisDao { int addStudent(Student student); int updateStudent(Student student); Student findStudentByNumber(String number); Student findStudentById(Long id); List<Student> findAll(); List<Student> findAllWithTeachers(); int selectTeachers(Long studentId, List<Long> teacherIds); }
說明:這裏我使用了查詢結果的繼承關係,當不須要查詢列表是resultMap就寫studentMap,當須要將教師信息同時加載時返回的resultMap就是studentTeacherMap。另外,多對多在插入關聯表的時候我使用的是批量插入,用了foreach,param2表明第二個集合參數
<?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必須指向Dao接口 --> <mapper namespace="com.xiaolong.demo.dao.StudentMyBatisDao"> <!--查詢結果不包含app列表--> <resultMap id="studentMap" type="com.xiaolong.demo.entity.Student"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="number" property="number" jdbcType="VARCHAR"/> <result column="gender" property="gender" jdbcType="VARCHAR"/> <result column="age" property="age" jdbcType="INTEGER"/> </resultMap> <!--查詢的時候將教師列表一塊兒查詢出來--> <resultMap id="studentTeacherMap" type="com.xiaolong.demo.entity.Student" extends="studentMap"> <collection property="teachers" ofType="com.xiaolong.demo.entity.Teacher"> <id property="id" column="t_id" jdbcType="BIGINT"/> <result property="name" column="t_name" jdbcType="VARCHAR"/> <result property="number" column="t_number" jdbcType="VARCHAR"/> <result property="gender" column="t_gender" jdbcType="VARCHAR"/> <result property="age" column="t_age" jdbcType="INTEGER"/> </collection> </resultMap> <sql id="columns"> id,name,number,gender,age </sql> <select id="findStudentByNumber" resultMap="studentMap" parameterType="java.lang.String"> SELECT <include refid="columns"/> FROM student WHERE number=#{0} </select> <select id="findStudentById" resultMap="studentTeacherMap" parameterType="java.lang.Long"> SELECT s.id,s.name,s.number,s.gender,s.age, t.id as t_id,t.name as t_name,t.number as t_number,t.gender as t_gender,t.age as t_age FROM student s LEFT JOIN student_teacher st ON s.id=st.s_id LEFT JOIN teacher t ON t.id=st.t_id WHERE s.id=#{0} </select> <select id="findAll" resultMap="studentMap"> SELECT * FROM student </select> <select id="findAllWithTeachers" resultMap="studentTeacherMap"> SELECT s.id,s.name,s.number,s.gender,s.age, t.id as t_id,t.name as t_name,t.number as t_number,t.gender as t_gender,t.age as t_age FROM student s LEFT JOIN student_teacher st ON s.id=st.s_id LEFT JOIN teacher t ON t.id=st.t_id </select> <insert id="addStudent" parameterType="com.xiaolong.demo.entity.Student"> insert into student ( name,number,gender,age ) values ( #{name,jdbcType=VARCHAR},#{number,jdbcType=VARCHAR},#{gender,jdbcType=VARCHAR},#{age,jdbcType=INTEGER} ) </insert> <update id="updateStudent" parameterType="com.xiaolong.demo.entity.Student"> update student <set> <if test="name != null"> name = #{name,jdbcType=VARCHAR}, </if> <if test="number != null"> number = #{number,jdbcType=VARCHAR}, </if> <if test="gender != null"> gender = #{gender,jdbcType=VARCHAR}, </if> <if test="age != null"> age = #{age,jdbcType=INTEGER}, </if> </set> where id=#{id,jdbcType=BIGINT} </update> <!--/*這種方式使用批量插入*/--> <insert id="selectTeachers"> INSERT INTO student_teacher (s_id,t_id) VALUES <foreach collection="param2" item="id" separator=","> (#{0}, #{id}) </foreach> </insert> </mapper>
功能包括:對學生的增刪改查以及學生選擇教師
public interface StudentService { public void addStudent(Student student) throws Exception; public void updateStudent(Student student) throws Exception; public Student findStudentByNumber(String number); public List<Student> findAll(); public List<Student> findAllWithTeachers(); public void selectTeachers(Long studentId,List<Long> teacherIds)throws Exception; }
在添加和修改是我手動判斷了學號是否重複
package com.xiaolong.demo.service.impl; import com.alibaba.fastjson.JSONObject; import com.xiaolong.demo.dao.StudentMyBatisDao; import com.xiaolong.demo.entity.Student; import com.xiaolong.demo.entity.Teacher; import com.xiaolong.demo.service.StudentService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * Created by xiaolong.zhu on 2015/6/4. */ @SuppressWarnings("ALL") @Transactional @Component public class StudentServiceImpl implements StudentService { private static final Logger logger = LoggerFactory.getLogger(StudentServiceImpl.class); @Autowired private StudentMyBatisDao studentMyBatisDao; @Override public void addStudent(Student student) throws Exception { logger.info("添加學生信息{}", JSONObject.toJSONString(student)); Student another = studentMyBatisDao.findStudentByNumber(student.getNumber()); if (another != null && another.getId() != student.getId()) throw new Exception("參數異常,number重複"); int result = studentMyBatisDao.addStudent(student); if (result != 1) throw new Exception("添加學生信息失敗"); } @Override public void updateStudent(Student student) throws Exception { logger.info("修改學生信息{}", JSONObject.toJSONString(student)); if (student.getId() == null) throw new Exception("參數異常,id爲null"); Student another = studentMyBatisDao.findStudentByNumber(student.getNumber()); if (another != null && another.getId() != student.getId()) throw new Exception("參數異常,number重複"); int result = studentMyBatisDao.updateStudent(student); if (result != 1) throw new Exception("修改學生信息失敗"); } @Override public Student findStudentByNumber(String number) { logger.info("經過學號{}查詢學生信息", number); Student student = studentMyBatisDao.findStudentByNumber(number); return student; } @Override public List<Student> findAll() { logger.info("查詢全部學生信息"); return studentMyBatisDao.findAll(); } @Override public List<Student> findAllWithTeachers() { logger.info("查詢全部學生信息及授課老師"); return studentMyBatisDao.findAllWithTeachers(); } @Override public void selectTeachers(Long studentId, List<Long> teacherIds) throws Exception { logger.info("學生{}選擇授課老師{}", studentId, teacherIds.toArray()); Student student = studentMyBatisDao.findStudentById(studentId); for (Teacher teacher : student.getTeachers()) { if (teacher != null) { if (teacherIds.contains(teacher.getId())) throw new Exception("參數異常,該學生" + studentId + "已經選擇了此教師" + teacher.getId() + ",不容許重複操做"); } } int result = studentMyBatisDao.selectTeachers(studentId, teacherIds); if (result != teacherIds.size()) throw new Exception("參數異常,教師id錯誤"); } }
在完成StudentServiceImpl以後,爲了在Controller中能用@Autowired
自動注入,須要將StudentServiceImpl在spring容器中註冊,見applicationContext.xml
說明:因爲用的spring mvc 4,因此@ResponseBody
就不用在寫了,它已經寫在了註解類@RestController
中了。另外,ip是獲取客戶端真是ip,若使用了代理,就從header中取得,未使用代理,就使用request.getRemoteHost()
獲得。
package com.xiaolong.demo.controller; import com.alibaba.fastjson.JSONObject; import com.xiaolong.demo.entity.Student; import com.xiaolong.demo.service.StudentService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.List; /** * Created by xiaolong.zhu on 2015/6/4. */ @SuppressWarnings("SpringJavaAutowiringInspection") @RestController @RequestMapping(value = "/demo/", consumes = {MediaType.ALL_VALUE}) public class StudentController { private static final Logger logger = LoggerFactory.getLogger(StudentController.class); @Autowired private StudentService studentService; @RequestMapping(value = "student", method = RequestMethod.POST, consumes = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity addStudent(@RequestBody Student student, HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null) ip = request.getRemoteHost(); logger.info("ip {} 正添加新的學生信息{}", ip, JSONObject.toJSONString(student)); try { studentService.addStudent(student); return new ResponseEntity(null, HttpStatus.OK); } catch (Exception e) { e.printStackTrace(); logger.info(e.getMessage()); JSONObject error = new JSONObject(); error.put("error", e.getMessage()); return new ResponseEntity(error, HttpStatus.BAD_REQUEST); } } @RequestMapping(value = "student", method = RequestMethod.PUT, consumes = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity updateStudent(@RequestBody Student student, HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null) ip = request.getRemoteHost(); logger.info("ip {} 正修改學生信息{}", ip, JSONObject.toJSONString(student)); try { studentService.updateStudent(student); return new ResponseEntity(null, HttpStatus.OK); } catch (Exception e) { e.printStackTrace(); logger.info(e.getMessage()); JSONObject error = new JSONObject(); error.put("error", e.getMessage()); return new ResponseEntity(error, HttpStatus.BAD_REQUEST); } } @RequestMapping(value = "student/{number}", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity findByNumber(@PathVariable("number") String number, HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null) ip = request.getRemoteHost(); logger.info("ip {} 正經過學號{}獲取學生信息", ip, number); Student student = studentService.findStudentByNumber(number); return new ResponseEntity(student, HttpStatus.OK); } @RequestMapping(value = "students", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity findAll(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null) ip = request.getRemoteHost(); logger.info("ip {} 獲取全部學生信息", ip); List<Student> students = studentService.findAll(); return new ResponseEntity(students, HttpStatus.OK); } @RequestMapping(value = "students/teachers", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity findAllWithTeachers(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null) ip = request.getRemoteHost(); logger.info("ip {} 獲取全部學生信息以及其授課老師", ip); List<Student> students = studentService.findAllWithTeachers(); return new ResponseEntity(students, HttpStatus.OK); } @RequestMapping(value = "student/{id}/teachers", method = RequestMethod.POST, consumes = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity selectTeachers(@PathVariable("id") Long studentId, @RequestBody JSONObject ids, HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null) ip = request.getRemoteHost(); logger.info("ip {} 學生{}選擇授課老師{}",ip,studentId,ids.toJSONString()); try { studentService.selectTeachers(studentId, (List<Long>) ids.get("teacherIds")); return new ResponseEntity(null, HttpStatus.OK); }catch (Exception e) { e.printStackTrace(); logger.info(e.getMessage()); JSONObject error = new JSONObject(); error.put("error", e.getMessage()); return new ResponseEntity(error, HttpStatus.BAD_REQUEST); } } }
全部接口集合:
1.測試添加學生信息:
2.測試修改學生信息:
3.測試按學號查詢學生信息:
4.測試查詢學生信息集合(不包含授課教師):
5.測試查詢學生信息集合(包含授課教師):
6.測試學生選擇教師(批量):
若學生重複選擇某一個教師,則不容許成功選擇教師
操做結果:
同窗生部分
同窗生部分
同窗生部分
同窗生部分
同窗生部分
同窗生部分
demo部署在我服務器使用以下ip地址及端口號便可調用上述全部接口
敬請期待….