javaEE回顾之MyBatis以及封装工具类(动态代理、sqlSession、UUID)和web应用

574 篇文章 4 订阅

使用MyBatis实现第一个web应用

主要目标:
	在web项目当中使用mybatis

1、创建一个web project,我这里使用的servlet版本号是servlet 3.1,
在servlet 3.0版本之后,web.xml文件就可以不写了,所有的配置均采用注解
来完成。

2、eclipse部署项目的时候,关于项目的名称:
	settings目录下的:org.eclipse.wst.common.component
		<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
		    <wb-module deploy-name="mybatis-004"> <!--部署时显示的名称-->
		        <wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
		        <wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
		        <property name="context-root" value="crm"/> <!--项目的真实名称-->
		        <property name="java-output-path" value="/mybatis-004/build/classes"/>
		    </wb-module>
		</project-modules>

3、关于mybatis核心对象的生命周期:
	SqlSessionFactoryBuilder:
		这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
		String resource = "org/mybatis/example/mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		
	SqlSessionFactory
		SqlSessionFactory 一旦被创建就应该在应用的运行期间“一直存在”,没有“任何理由”对它进行清除或重建。
		SqlSessionFactory在整个容器当中只允许出现一个。
		
		一个SqlSessionFactory对象,代表了一个数据库,当多数据库开发的时候,SqlSessionFactory对象才会实例化多个。
		对于单一数据库来说,只能有一个。
		
	SqlSession
		每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,
		所以它的最佳的作用域是请求或方法作用域。
	
	原则:
		一个线程一个Connection。 (JDBC)
		一个线程一个SqlSession。(MyBatis)
	
	
2、步骤:
	2.1、 导入相关jar
		log4j-1.2.17.jar
		mybatis-3.4.5.jar
		mysql-connector-java-5.1.23-bin.jar
	2.2、 引入相关配置文件
		jdbc.properties
		log4j.properties
		mybatis-config.xml
		SqlMapper.xml
	2.3、数据库表我们就直接使用tbl_student了。
		tbl_student
		id	name 		birth
		-------------------------------
		1	zhangsan	1980-11-11
		...
		id采用UUID。
	2.4、准备一个UUID的工具类。
	2.5、提供一个mybatis的工具类:SqlSessionUtil
		SqlSession需要放到ThreadLocal当中,必须保证一个线程一个SqlSession对象。
	2.6、动态代理(JDK动态代理,实现事务的自动管理。)
		事务在service层控制,所以以后我们不能直接调用service,
		需要先创建一个service对象的代理对象,通过代理对象执行service
		当中的方法。
	2.7、准备前端页面
		save.jsp
		准备form表单
	2.8、web.xml文件的编写,但是我们项目中没有web.xml文件。使用注解。
	2.9、新建MVC所需要的package
		domain
		controller
		service.impl
		dao.impl
		
	2.10、提供StudentController、StudentService、StudentDao
	
	2.11、SqlMapper.xml文件一般和dao接口放一块,并且该xml文件的名称一般和dao接口名称一致。
	不是必须的,只是开发规范。
	
	2.12、在mybatis核心文件中配置SqlMapper.xml文件的路径。
		<mappers>
			<mapper resource="com/wkcto/crm/dao/StudentDao.xml"/>
		</mappers>
	
	2.13、在sqlmapper.xml文件中,编写sql语句:
		<insert id="saveStu" parameterType="com.wkcto.crm.domain.Student">
			insert into tbl_student
				(id,name,birth)
			values
				(#{id},#{name},#{birth})
		</insert>
	
--------------------------------------------------------------------------------------
	
	<!-- 别名 -->
	<typeAliases>
		<typeAlias type="com.wkcto.crm.domain.Student" alias="Student"/>
	</typeAliases>
	

JavaEE常用工具类如下:

package com.wkcto.setutil;

import java.io.BufferedReader;
import java.io.FileReader;
/**
Set工具类
*/
public class Test {

	public static void main(String[] args) throws Exception{
		BufferedReader br = new BufferedReader(new FileReader("info"));
		/*
		while(true){
			String temp = br.readLine();
			if(temp == null){
				break;
			}
			System.out.println(temp);
		}
		*/
		
		String temp = null;
		while((temp = br.readLine()) != null){
			System.out.println("d.set" + temp.substring(0, 1).toUpperCase() + temp.substring(1) + "(" + temp + ");");
		}
		
		br.close();
	}

}

package com.wkcto.crm.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ExcelWriter<T> {

	/**
	 * 根据数据返回workbook
	 * @param dataList 数据
	 * @param sheetName 表格名称
	 * @param clazz 类型
	 * @return 工作簿
	 */
	public XSSFWorkbook getWorkbook(List<T> dataList , String sheetName , Class clazz) {
		XSSFWorkbook workbook = new XSSFWorkbook();
		XSSFSheet sheet = workbook.createSheet(sheetName);
		XSSFRow row0 = sheet.createRow(0);
		Field[] fields = clazz.getDeclaredFields(); 
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			String fieldName = field.getName();
			XSSFCell cell = row0.createCell(i);
			cell.setCellValue(fieldName);
		}
		try {
			for (int i = 0; i < dataList.size(); i++) {
				T obj = dataList.get(i);
				XSSFRow row = sheet.createRow(i + 1);
				for (int j = 0; j < fields.length; j++) {
					String propertyName = fields[j].getName();
					String getMethodName = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
					Method getMethod = clazz.getDeclaredMethod(getMethodName);
					Object value = getMethod.invoke(obj);
					XSSFCell cell = row.createCell(j);
					cell.setCellValue(value == null ? "" : value.toString());
				}
			}
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return workbook;
	}
}

package com.wkcto.crm.utils;

import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ExcelReader<T> {

	/**
	 * 读取excel文件,获取List集合
	 * @param excelPath
	 * @param clazz
	 * @return
	 */
	public List<T> read(String excelPath, Class clazz) {
		List<T> dataList = new ArrayList<>();
		try {
			Workbook workbook = null;
			if (excelPath.indexOf(".xlsx") > -1) {
				workbook = new XSSFWorkbook(new FileInputStream(excelPath));
			} else {
				workbook = new HSSFWorkbook(new FileInputStream(excelPath));
			}
			Sheet sheet = workbook.getSheetAt(0);
			Row row0 = sheet.getRow(0);
			int totalColumns = row0.getLastCellNum();
			String[] propertyNames = new String[totalColumns];
			for (int i = 0; i < totalColumns; i++) {
				Cell cell = row0.getCell(i);
				String cellValue = getCellValue(cell);
				propertyNames[i] = cellValue;
			}
			int totalRows = sheet.getPhysicalNumberOfRows();
			for (int i = 1; i < totalRows; i++) {
				Row row = sheet.getRow(i);
				if (row != null) {
					T obj = (T)clazz.newInstance();
					for (int j = 0; j < totalColumns; j++) {
						Cell cell = row.getCell(j);
						String cellValue = getCellValue(cell);
						String propertyName = propertyNames[j];
						String methodName = "set" + propertyName.substring(0, 1).toUpperCase()
								+ propertyName.substring(1);
						Method setMethod = clazz.getDeclaredMethod(methodName, String.class);
						setMethod.invoke(obj, cellValue);
					}
					dataList.add(obj);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return dataList;
	}
	
	/**
	 * 根据单元格获取单元格当中的文本
	 * @param cell
	 * @return
	 */
	private static String getCellValue(Cell cell) {
		DecimalFormat df = new DecimalFormat("#");
		if (cell == null)
			return "";
		switch (cell.getCellType()) {
		case Cell.CELL_TYPE_NUMERIC:
			if (DateUtil.isCellDateFormatted(cell)) {
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				return sdf.format(cell.getDateCellValue()).toString();
				// return
				// sdf.format(DateUtil.getJavaDate(cell.getNumericCellValue())).toString();
			}
			return df.format(cell.getNumericCellValue());
		case Cell.CELL_TYPE_STRING:
			// System.out.println(cell.getStringCellValue());
			return cell.getStringCellValue();
		case Cell.CELL_TYPE_FORMULA:
			return cell.getCellFormula();
		case Cell.CELL_TYPE_BLANK:
			return "";
		case Cell.CELL_TYPE_BOOLEAN:
			return cell.getBooleanCellValue() + "";
		case Cell.CELL_TYPE_ERROR:
			return cell.getErrorCellValue() + "";
		}
		return "";
	}

}

import java.text.SimpleDateFormat;
import java.util.Date;


public class DateUtil
{
  public static String getsysTime(){//系统时间生成工具
  
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
  
  }


}
package com.wkcto.crm.utils;

/**
 * 常量类。系统常量,所有项目中的常量都放到这个类当中。
 * @author Administrator
 *
 */
public class Const {

	/**
	 * 登录成功之后,将用户对象绑定到session域时使用的name。
	 */
	public static final String SESSION_USER = "user";
	
}


package com.wkcto.crm.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5 {
	
	/**
	 * 生成32位md5码
	 * @param password
	 * @return
	 */
	public static String get(String password) {

		try {
			// 得到一个信息摘要器
			MessageDigest digest = MessageDigest.getInstance("md5");
			byte[] result = digest.digest(password.getBytes());
			StringBuffer buffer = new StringBuffer();
			// 把每一个byte 做一个与运算 0xff;
			for (byte b : result) {
				// 与运算
				int number = b & 0xff;// 加盐
				String str = Integer.toHexString(number);
				if (str.length() == 1) {
					buffer.append("0");
				}
				buffer.append(str);
			}

			// 标准的md5加密后的结果
			return buffer.toString();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return "";
		}

	}
}

package com.wkcto.crm.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

/**
 * Bean工厂
 * @author Administrator
 *
 */
public class BeanFactory {
	
	/**
	 * 将request中的数据取出赋值给javabean对象的相关属性,使用该<br>
	 * 工具的时候要注意form表单提交的参数name必须和javabean的属性名一致。
	 * @param request
	 * @param clazz
	 * @return
	 */
	public static Object getBean(HttpServletRequest request , Class clazz){
		// 创建javabean对象
		Object obj = null;
		try {
			// ...save.do?username=zhangsan&birth=2010-10-10&sex=1....
			// 获取所有的参数name
			Enumeration<String> names = request.getParameterNames();
			obj = clazz.newInstance();
			while(names.hasMoreElements()){
				try {
					// 获取了参数name
					String name = names.nextElement(); // customerName
					// 获取参数value
					String value = request.getParameter(name); // 2010-10-10
					// 给obj对象的相关属性赋值
					String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
					Method setMethod = clazz.getDeclaredMethod(methodName, String.class);
					setMethod.invoke(obj, value);
				} catch (NoSuchMethodException e) {
					// 异常可以让程序更加健壮,出问题之后不影响程序的继续执行。
					// 出现异常之后,提示异常信息,但不要耽误“生产”。
				}
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return obj;
	}
	
}

package com.wkcto.crm.utils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 响应json工具类
 * @author Administrator
 *
 */
public class OutJson {
	
	private OutJson(){
		
	}
	
	/**
	 * 响应json,输出结果为:{"success" : true}或者{"success" : false}
	 * @param response
	 * @param success
	 */
	public static void print(HttpServletResponse response , boolean success){
		try {
			Map<String, Boolean> jsonMap = new HashMap<>();
			jsonMap.put("success", success);
			ObjectMapper om = new ObjectMapper();
			String json = om.writeValueAsString(jsonMap);
			response.setContentType("text/json;charset=UTF-8");
			response.getWriter().print(json);
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 响应json
	 * @param response
	 * @param jsonMap
	 */
	public static void print(HttpServletResponse response , Object jsonMap){
		try {
			ObjectMapper om = new ObjectMapper();
			String json = om.writeValueAsString(jsonMap);
			response.setContentType("text/json;charset=UTF-8");
			response.getWriter().print(json);
		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
 
package com.wkcto.crm.utils;

import java.io.IOException;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * mybatis的工具类。
 * @author Administrator
 *
 */
public class SqlSessionUtil {
	
	private static SqlSessionFactory factory;
	private static ThreadLocal<SqlSession> local = new ThreadLocal<>();
	
	/**
	 * 类加载的时候执行,创建一个SqlSessionFactory对象。
	 */
	static{
		try {
			factory = new SqlSessionFactoryBuilder()
					.build(Resources.getResourceAsStream("mybatis-config.xml"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取当前线程中的SqlSession对象
	 * @return
	 */
	public static SqlSession getCurrentSqlSession(){
		// 先从当前线程中取SqlSession对象
		SqlSession sqlSession = local.get();
		if(sqlSession == null){ // 从当前线程中没有取到SqlSession对象
			sqlSession = factory.openSession(); // 获取新的SqlSession对象
			local.set(sqlSession); // 将SqlSession对象绑定到当前线程中。
		}
		return sqlSession;
	}
	
	/**
	 * 
	 * @param sqlSession
	 */
	public static void rollback(SqlSession sqlSession){
		if(sqlSession != null){
			sqlSession.rollback(); 
		}
	}
	
	/**
	 * 
	 * @param sqlSession
	 */
	public static void close(SqlSession sqlSession){
		if(sqlSession != null){
			sqlSession.close();
			local.remove(); // 记得这行代码(SqlSession对象和当前线程解除绑定)
		}
	}
}


package com.wkcto.crm.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.ibatis.session.SqlSession;

public class TransactionHandler implements InvocationHandler {

	private Object target; // 真正的service对象(目标对象)
	
	public TransactionHandler(Object target) {
		this.target = target;
	}
	
	/**
	 * 专门用来获取代理对象的。
	 * @return
	 */
	public Object getProxy(){
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		SqlSession sqlSession = null;
		Object retValue = null;
		try {
			sqlSession = SqlSessionUtil.getCurrentSqlSession(); // 开启事务
			// 真正调用service
			retValue = method.invoke(target, args);
			sqlSession.commit(); // 提交事务
		} catch (Exception e) {
			SqlSessionUtil.rollback(sqlSession);
			e.printStackTrace();
		} finally{
			SqlSessionUtil.close(sqlSession);
		}
		return retValue;
	}

}

package com.wkcto.crm.utils;

import java.util.UUID;

/**
 * UUID生成器
 * @author Administrator
 *
 */
public class UUIDGenerator { // ....tor ....器,例如:Comparator:比较器
	
	// UUIDGenerator采用名词,表示UUID生成器
	private UUIDGenerator(){
		
	}
	
	// generate动词:表示生成
	public static String generate(){
		return UUID.randomUUID().toString().replaceAll("-", "");
	}
	
}

domain包

package com.wkcto.crm.domain;

public class Student {
	private String id;
	private String name;
	private String birth;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getBirth() {
		return birth;
	}

	public void setBirth(String birth) {
		this.birth = birth;
	}

}

controller包

package com.wkcto.crm.web.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.wkcto.crm.domain.Student;
import com.wkcto.crm.service.StudentService;
import com.wkcto.crm.service.impl.StudentServiceImpl;
import com.wkcto.crm.utils.TransactionHandler;
import com.wkcto.crm.utils.UUIDGenerator;

// @WebFilter
// @WebListener

@WebServlet(urlPatterns={"/student/save.do"})
public class StudentController extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request, response);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 接收表单参数
		String id = UUIDGenerator.generate();
		String name = request.getParameter("name");
		String birth = request.getParameter("birth");
		Student s = new Student();
		s.setId(id);
		s.setName(name);
		s.setBirth(birth);
		// 获取service代理对象
		// 不能这样:StudentService studentService = new StudentServiceImpl(); //这样事务失效了。
		StudentService studentService = (StudentService)new TransactionHandler(new StudentServiceImpl()).getProxy();	
		// 调用代理方法
		boolean ok = studentService.save(s);
		// 响应JSON
		response.getWriter().print("{\"success\" : "+ok+"}");
	}
}
 

service包

package com.wkcto.crm.service;

import com.wkcto.crm.domain.Student;

public interface StudentService {

	/**
	 * 保存学生
	 * @param s
	 * @return
	 */
	boolean save(Student s);

}

serviceimpl包

package com.wkcto.crm.service.impl;

import com.wkcto.crm.dao.StudentDao;
import com.wkcto.crm.dao.impl.StudentDaoImpl;
import com.wkcto.crm.domain.Student;
import com.wkcto.crm.service.StudentService;

public class StudentServiceImpl implements StudentService {

	private StudentDao studentDao = new StudentDaoImpl(); 
			
	@Override
	public boolean save(Student s) {
		return studentDao.save(s) == 1;
	}

}

dao包

package com.wkcto.crm.dao;

import com.wkcto.crm.domain.Student;

public interface StudentDao {

	/**
	 * 保存学生
	 * @param s
	 * @return 1表示成功,其他值表示失败
	 */
	int save(Student s);

}

daoImpl

package com.wkcto.crm.dao.impl;

import com.wkcto.crm.dao.StudentDao;
import com.wkcto.crm.domain.Student;
import com.wkcto.crm.utils.SqlSessionUtil;

public class StudentDaoImpl implements StudentDao {

	@Override
	public int save(Student s) {
		return SqlSessionUtil.getCurrentSqlSession().insert("saveStu", s);
	}

}

sql配置文件

<?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="fdsjkafldjkslafds">

	<insert id="saveStu" parameterType="Student">
		insert into tbl_student
			(id,name,birth)
		values
			(#{id},#{name},#{birth})
	</insert>
	
</mapper>


jsp页面

<%@page contentType="text/html; charset=utf-8"%>
<!doctype html>
<html>
	<head>
		<title>mybatis-save</title>
	</head>
	<body>
		
		<form action="${pageContext.request.contextPath }/student/save.do" method="post">
			学生姓名
				<input type="text" name="name">
				<br>
			学生生日
				<input type="text" name="birth">
				<br>
			<input type="submit" value="保存">
		</form>
		
	</body>
</html>

MyBatis可以使用“动态代理”机制自动生成dao接口的实现类

MyBatis可以使用“动态代理”机制自动生成dao接口的实现类,以后dao实现类不需要我们程序员提供了:

	要使用mybatis的这种机制,必须做到以下几点:
	
		1private StudentDao studentDao = SqlSessionUtil.getCurrentSqlSession().getMapper(StudentDao.class);
		
		2、SqlMapper.xml文件中的namespace必须是dao接口的全限定接口名:
			<mapper namespace="com.wkcto.crm.dao.StudentDao">
			
		3、dao接口中的方法名必须和Sql语句的id一致:
			public interface StudentDao{
				int save(Student s);
			}
			<insert id="save" parameterType="Student">
			
		4、在mybatis-config.xml文件中,mappers标签中使用package机制:
			<package name="com.wkcto.crm.dao"/>
			mybatis会自动扫描com.wkcto.crm.dao包下的资源,来生成代理类。
			
		5、StudentDao.class和StudentDao.xml要放到dao包下,放在一起。
		
package com.wkcto.crm.service.impl;

import com.wkcto.crm.dao.StudentDao;
import com.wkcto.crm.domain.Student;
import com.wkcto.crm.service.StudentService;
import com.wkcto.crm.utils.SqlSessionUtil;

public class StudentServiceImpl implements StudentService {
//1.修改这里
	private StudentDao studentDao = SqlSessionUtil.getCurrentSqlSession().getMapper(StudentDao.class); 
			
	@Override
	public boolean save(Student s) {
		return studentDao.save(s) == 1;
	}

}
// 2.修改这里
<?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.wkcto.crm.dao.StudentDao">

	<insert id="save" parameterType="Student">
		insert into tbl_student
			(id,name,birth)
		values
			(#{id},#{name},#{birth})
	</insert>
	
</mapper>

```cpp
package com.wkcto.crm.dao;

import com.wkcto.crm.domain.Student;

public interface StudentDao {

	/**
	 * 保存学生
	 * @param s
	 * @return 1表示成功,其他值表示失败
	 */
	 //3.修改这里
	int save(Student s);

}

<?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>
	<!-- 引入外部独立的属性配置文件 -->
	<properties resource="jdbc.properties"/>
	
	<!-- 别名 -->
	<typeAliases>
		<typeAlias type="com.wkcto.crm.domain.Student" alias="Student"/>
	</typeAliases>
	
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.user}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
	//4.修改这里
		<package name="com.wkcto.crm.dao"/>
	</mappers>
</configuration>

//5.同在一个包下面

StudentDao.java和Student.xml在同一个包下面

基础应用 1:给占位符传值

关于给mybatis的sql语句占位符传值:
1、parameterType这个属性是专门给sql语句传值
2、parameterType常见的有这几种传值方式:
* 简单类型:parameterType属性可以省略。简单类型包括基本数据类型以及对应的包装类型,还有String。
* javabean
* Map

3、dao接口的方法上形式参数列表的个数不止一个,大于1个的时候,怎么给sql语句传值?
public interface StudentDao{
int save(String id,String name,String birth);
}

	解决方案:
		1、把以上程序改一改,id,name,birth封装到javabean中。
		2、把以上程序改一改,id,name,birth封装到Map中。
		3、现在程序的参数个数不多,两三个,不想放到Map当中,不想封装javabean,怎么办?
			<insert id="save1">
				insert into tbl_student
					(id,name,birth)
				values
					(#{arg0},#{arg1},#{arg2})
			</insert>
			
			其中arg0表示第一个参数,arg1表示第二个参数,以此类推。
			
			注意:这种方式要求每一个参数必须是简单类型。
			mybatis3.4版本之前不是以上的写法。是这样写的:
				<insert id="save1">
					insert into tbl_student
						(id,name,birth)
					values
						(#{0},#{1},#{2})
				</insert>
			
	
	dao接口的实现类使用mybatis动态代理自动生成之后,就不再需要手动编写以下方法了:
		sqlSession.insert("sqlId" , 1个参数) // 要么这个1个参数是简单类型,要么是javabean,要么是Map。
		sqlSession.delete("sqlId" , 1个参数)
		sqlSession.update("sqlId" , 1个参数)
		sqlSession.selectOne("sqlId" , 1个参数)
		sqlSession.selectList("sqlId" , 1个参数)
		sqlSession.selectList("sqlId")

1、关于MyBatis中的动态sql:

以及

2、关于动态sql当中的where和if,我们以分页查询为例


	foreach
	where
	if
2、关于foreach:

	2.1、collection="array"
		<delete id="deleteById">
			delete from tbl_student where id in
			<foreach collection="array" open="(" close=")" separator="," item="stuId">
			#{stuId}
			</foreach>
		</delete>
		
	2.2、collection="list"
		一次向数据库表当中插入多条数据,例如:导入。
		insert into tbl_student(id,name,birth) values('1','zhangsan','1999-11-11'),('2','lisi','2000-11-11');
		
		<insert id="saves" parameterType="Student">
			insert into tbl_student
				(id,name,birth)
			values
			<foreach collection="list" separator="," item="o">
				(#{o.id},#{o.name},#{o.birth})
			</foreach>
		</insert>
		
3、

## 关于动态sql当中的where和if,我们以分页查询为例

讲解:

	分析:1、客户端向服务器提交哪些数据?2、服务器最终响应什么数据到浏览器?
	
	1、客户端向服务器提交哪些数据?
		查询条件 + 页码 + 每页显示的记录条数
		我们这里的查询条件:按照学生姓名模糊查询,按照学生生日精确查询。
		提交:name、birth、pageNo、pageSize
		
		浏览器提交以上的条件给服务器,服务器控制器Controller获取到这些查询条件之后,怎么传给service?
			String name = request.getParameter("name");
			String birth = request.getParameter("birth");
			Integer pageNo = Integer.valueOf(request.getParameter("pageNo"));
			Integer pageSize = Integer.valueOf(request.getParameter("pageSize"));
			//4个条件怎么传给service?
			Map<String,Object> map = ...;
			map.put("name",name);
			map.put("birth",birth);
			map.put("startIndex",(pageNo-1) * pageSize);
			map.put("pageSize",pageSize);
			
	2、服务器最终响应什么数据到浏览器?
		JSON。
		什么格式的JSON?
			{
				"total" : 400 , 
				"dataList" : [{"id":"","name":"","birth":""},{"id":"","name":"","birth":""},{"id":"","name":"","birth":""}]
			}
			
		返回的是:
			符合“查询条件”的总记录条数。
			符合“查询条件”的“当前页”数据。
			
	3、service分页查询执行结束之后,需要返回给Controller什么数据?
		service返回一个Map集合:
			Map<String,Object>
			key			value
			---------------------------
			"total"		400 --------------------> 调用dao获取符合条件的总记录条数
			"dataList"	dataList ---------------> 调用dao获取符合条件的当前页数据
	
	4、dao包括两条sql语句:(在dao中有这几个条件,是service传过来的:name、birth、startIndex、pageSize)
		
		第一条sql语句:查询符合条件的总记录条数
			<select parameterType="Map">
				select
					count(*)
				from
					tbl_student
				<where>
					<if test="name != null and name != ''">
						and name like '%' #{name} '%'	
					</if>
					<if test="birth != null and birth != ''">
						and birth = #{birth}
					</if>
				</where>
			</select>
		第二条sql语句:查询符合条件的当前页数据
			<select parameterType="Map">
				select
					*
				from
					tbl_student
				<where>
					<if test="name != null and name != ''">
						and name like '%' #{name} '%'	
					</if>
					<if test="birth != null and birth != ''">
						and birth = #{birth}
					</if>
				</where>
				order by 
					name asc
				limit
					#{startIndex} , #{pageSize}
			</select>
				
	5、分页查询的“起始位置”和“长度”的研究:
		每页显示3条记录
		第1页:0 , 32页:3 , 33页:6 , 34页:9 , 3
		......
		
		每页显示pageSize条记录
		第pageNo页:(pageNo-1) * pageSize , pageSize
		
		
4、以上的程序只是开发了“服务”,对外提供接口:

	接口API:
		接口地址:
			http://localhost:8080/crm/student/page.do
		接口参数:
			pageNo	页码
			pageSize	每页显示的记录条数
			name	姓名,支持模糊查询
			birth	生日
		接口的返回数据:
			{
				"total": 1,
				"dataList": [{
					"id": "64c37a8d0f2c4732916d08011932ac8d",
					"name": "王五",
					"birth": "1911-11-12"
				}]
			}
		
	语言之间交换数据的标准包括:
		json 和 xml
		
		json轻量级,体积小,解析方便,语法松散,不严格。
		xml重量级,体积大,解析麻烦,语法严格,一般银行的项目都会使用XML。
	
	注意:这里所描述的接口不是java中的interface。
			


3.jackson插件

在这里插入图片描述

代码演示

package com.wkcto.crm.web.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.wkcto.crm.domain.Student;
import com.wkcto.crm.service.StudentService;
import com.wkcto.crm.service.impl.StudentServiceImpl;
import com.wkcto.crm.utils.TransactionHandler;
import com.wkcto.crm.utils.UUIDGenerator;

// @WebFilter
// @WebListener

@WebServlet(urlPatterns={"/student/save.do","/student/save1.do","/student/delete.do","/student/saves.do" , "/student/page.do"})
public class StudentController extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request, response);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String servletPath = request.getServletPath();
		if("/student/save.do".equals(servletPath)){
			doSave(request,response);
		} else if("/student/save1.do".equals(servletPath)){
			doSave1(request,response);
		} else if("/student/delete.do".equals(servletPath)){
			doDel(request,response);
		} else if("/student/saves.do".equals(servletPath)){
			doSaves(request,response);
		} else if("/student/page.do".equals(servletPath)){
			doPage(request,response);
		}
	}
	
	protected void doPage(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 接收前端的条件
		String name = request.getParameter("name");
		String birth = request.getParameter("birth");
		Integer pageNo = Integer.valueOf(request.getParameter("pageNo"));
		Integer pageSize = Integer.valueOf(request.getParameter("pageSize"));
		// 将条件放到Map集合中
		Map<String,Object> conditionMap = new HashMap<>();	
		conditionMap.put("name1", name);
		conditionMap.put("birth1", birth);
		conditionMap.put("startIndex", (pageNo - 1) * pageSize);
		conditionMap.put("pageSize1", pageSize);
		// 调用service
		StudentService studentService = (StudentService)new TransactionHandler(new StudentServiceImpl()).getProxy();
		Map<String,Object> pageMap = studentService.getPageByCondition(conditionMap);
		// 响应JSON。
		// 将pageMap转换成json,响应
		ObjectMapper om = new ObjectMapper();
		String json = om.writeValueAsString(pageMap);
		response.setContentType("text/json;charset=UTF-8"); // 设置响应的内容类型为json,字符集采用U8
		response.getWriter().print(json);
	}
	
	protected void doSaves(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		// 准备一个List集合,添加多个学生
		List<Student> stuList = new ArrayList<>();
		Student s1 = new Student();
		s1.setId(UUIDGenerator.generate());
		s1.setName("王五");
		s1.setBirth("1911-11-12");
		
		Student s2 = new Student();
		s2.setId(UUIDGenerator.generate());
		s2.setName("杰克");
		s2.setBirth("2000-11-12");
		
		stuList.add(s1);
		stuList.add(s2);
		
		StudentService studentService = (StudentService)new TransactionHandler(new StudentServiceImpl()).getProxy();
		boolean ok = studentService.saves(stuList);
		
		response.getWriter().print("{\"success\" : "+ok+"}");
	}
	
	protected void doDel(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 获取id
		String[] ids = request.getParameterValues("id");
		// 调用service删除
		StudentService studentService = (StudentService)new TransactionHandler(new StudentServiceImpl()).getProxy();
		boolean ok = studentService.deleteById(ids);
		// 响应JSON
		response.getWriter().print("{\"success\" : "+ok+"}");
	}
	
	protected void doSave1(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String id = UUIDGenerator.generate();
		String name = request.getParameter("name");
		String birth = request.getParameter("birth");
		StudentService studentService = (StudentService)new TransactionHandler(new StudentServiceImpl()).getProxy();
		boolean ok = studentService.save1(id,name,birth);
		response.getWriter().print("{\"success\" : "+ok+"}");
	}
	
	protected void doSave(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 接收表单参数
		String id = UUIDGenerator.generate();
		String name = request.getParameter("name");
		String birth = request.getParameter("birth");
		Student s = new Student();
		s.setId(id);
		s.setName(name);
		s.setBirth(birth);
		// 获取service代理对象
		// 不能这样:StudentService studentService = new StudentServiceImpl(); //这样事务失效了。
			onHandler(new StudentServiceImpl()).getProxy();	
		// 调用代理方法
		boolean ok = studentService.save(s);
		// 响应JSON
		response.getWriter().print("{\"success\" : "+ok+"}");
	}
}
 
package com.wkcto.crm.service;

import java.util.List;
import java.util.Map;

import com.wkcto.crm.domain.Student;

public interface StudentService {

	/**
	 * 保存学生
	 * @param s
	 * @return
	 */
	boolean save(Student s);

	/**
	 * 保存学生
	 * @param id
	 * @param name
	 * @param birth
	 * @return
	 */
	boolean save1(String id, String name, String birth);

	/**
	 * 根据id删除多个学生
	 * @param ids
	 * @return
	 */
	boolean deleteById(String[] ids);

	/**
	 * 保存多个学生
	 * @param stuList
	 * @return
	 */
	boolean saves(List<Student> stuList);

	/**
	 * 根据分页查询的条件获取分页数据
	 * @param conditionMap
	 * @return
	 */
	Map<String, Object> getPageByCondition(Map<String, Object> conditionMap);

}

 
package com.wkcto.crm.service.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.wkcto.crm.dao.StudentDao;
import com.wkcto.crm.domain.Student;
import com.wkcto.crm.service.StudentService;
import com.wkcto.crm.utils.SqlSessionUtil;

public class StudentServiceImpl implements StudentService {

	private StudentDao studentDao = SqlSessionUtil.getCurrentSqlSession().getMapper(StudentDao.class); 
			
	@Override
	public boolean save(Student s) {
		return studentDao.save(s) == 1;
	}

	@Override
	public boolean save1(String id, String name, String birth) {
		return studentDao.save1(id,name,birth) == 1;
	}

	@Override
	public boolean deleteById(String[] ids) {
		return studentDao.deleteById(ids) == ids.length;
	}

	@Override
	public boolean saves(List<Student> stuList) {
		return studentDao.saves(stuList) == stuList.size();
	}

	@Override
	public Map<String, Object> getPageByCondition(Map<String, Object> conditionMap) {
		
		Map<String, Object> pageMap = new HashMap<>();
		
		// 存放符合查询条件的总记录条数
		Long total = studentDao.getTotalByCondition(conditionMap);
		pageMap.put("total", total);
		
		// 存放符合查询条件的当前页数据
		List<Student> dataList = studentDao.getDataListByCondition(conditionMap);
		pageMap.put("dataList", dataList);
		
		return pageMap;
	}

}

 
package com.wkcto.crm.dao;

import java.util.List;
import java.util.Map;

import com.wkcto.crm.domain.Student;

public interface StudentDao {

	/**
	 * 保存学生
	 * @param s
	 * @return 1表示成功,其他值表示失败
	 */
	int save(Student s);

	/**
	 * 保存学生
	 * @param id
	 * @param name
	 * @param birth
	 * @return
	 */
	int save1(String id, String name, String birth);

	/**
	 * 根据id删除多个学生
	 * @param ids
	 * @return
	 */
	int deleteById(String[] ids);

	/**
	 * 保存多个学生
	 * @param stuList
	 * @return
	 */
	int saves(List<Student> stuList);

	/**
	 * 根据查询条件获取符合条件的总记录条数
	 * @param conditionMap
	 * @return
	 */
	Long getTotalByCondition(Map<String, Object> conditionMap);

	/**
	 * 根据查询条件获取符合条件的当前页数据
	 * @param conditionMap
	 * @return
	 */
	List<Student> getDataListByCondition(Map<String, Object> conditionMap);

}

<?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.wkcto.crm.dao.StudentDao">

	<insert id="save" parameterType="Student">
		insert into tbl_student
			(id,name,birth)
		values
			(#{id},#{name},#{birth})
	</insert>
	
	<insert id="save1">
		insert into tbl_student
			(id,name,birth)
		values
			(#{arg0},#{arg1},#{arg2})
	</insert>
	
	<delete id="deleteById">
		delete from tbl_student where id in
		<foreach collection="array" open="(" close=")" separator="," item="stuId">
		#{stuId}
		</foreach>
		<!-- 
			open以什么开始
			close以什么结束
			collection="array" 表示参数是一个数组
			separator 用来指定分隔符
			item 代表循环过程中collection中的每一个元素
		 -->
	</delete>
	
	<insert id="saves" parameterType="Student">
		insert into tbl_student
			(id,name,birth)
		values
		<foreach collection="list" separator="," item="o">
			(#{o.id},#{o.name},#{o.birth})
		</foreach>
	</insert>
	
	<select id="getTotalByCondition" parameterType="Map" resultType="Long">
		select
			count(*)
		from
			tbl_student
		<where>
			<if test="name1 != null and name1 != ''">
				and name like '%' #{name1} '%'
			</if>
			<if test="birth1 != null and birth1 != ''">
				and birth = #{birth1}
			</if>
		</where>
	</select>
	
	<select id="getDataListByCondition" parameterType="Map" resultType="Student">
		select
			*
		from
			tbl_student
		<where>
			<if test="name1 != null and name1 != ''">
				and name like '%' #{name1} '%'  <!-- mybatis的占位符“#{}”两边必须有空格,没有空格的时候mybatis无法检测到占位符。 -->
			</if>
			<if test="birth1 != null and birth1 != ''">
				and birth = #{birth1}
			</if>
		</where>
		order by
			name asc
		limit
			#{startIndex} , #{pageSize1}
	</select>
		
</mapper>

 

到此为止,mybatis还有两个知识点:
1、#{} 和 ${}的区别
以下是网友1解答:

原sql语句:
delete from
ups_role_permission_dataparams
where role_id = #{roleId,jdbcType=INTEGER}
在这里用到了#{},使用#时:

1、用来传入参数,sql在解析的时候会加上" ",当成字符串来解析 ,如这里 role_id = "roleid";

2、#{}能够很大程度上防止sql注入;

延伸:
1、用${}传入数据直接显示在生成的sql中,如上面的语句,用role_id = ${roleId,jdbcType=INTEGER},那么sql在解析的时候值为role_id = roleid,执行时会报错;

2、${}方式无法防止sql注入;

3、$一般用入传入数据库对象,比如数据库表名;

4、能用#{}时尽量用#{};

注意:Mybaties排序时使用order by 动态参数时需要注意,使用${}而不用#{}

以下是网友2解答

两者都可以在mybatis中用在输入映射

{}是预编译处理,
${}是字符串替换。

mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值,完成的是简单的字符串拼接。

补充:在mybatis中使用#{}可以防止sql注入,提高系统安全性。

sql注入是什么:
例如:用户输入的账号密码在代码中是以字符串拼接的方式生成查询语句的,这样用户输入的内容很容易改变我们的原查询代码,这就相当一一个数据库注入问题。
我们验证密码和账号的时候,如果用户在输入密码的时候 输入 or 1=1;那不管他输入的密码是什么都可以通过。

本人采纳网友2解释。

2、关于mybatis-config.xml文件中相关的配置说明。

中文文档链接:
点击前往

在这里插入图片描述

MyBatis 可以配置成适应多种环境

,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:

每个数据库对应一个 SqlSessionFactory 实例

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为

数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):

插件(plugins)

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

类型处理器(typeHandlers)

MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

提示 从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值