zoukankan      html  css  js  c++  java
  • 项目中使用 MyBatis(二)

    1. 项目中使用 MyBatis(二)

    1.1. 获取新插入的数据的id

    如果在插入数据时,就需要实时获取新数据的id,首先,在配置XML时,<insert>节点需要添加2个属性的配置:

    <insert id="xx" parameterType="xx.xx.xx.xx.User"
    	useGeneratedKeys="true"
    	keyProperty="id">
    </insert>
    

    以上配置中,useGeneratedKeys表示需要获取自动生成的主键(通常都是自动递增的id),keyProperty表示当获取了主键的值(id的值),该值将被封装到哪个属性中,即插入数据时的参数User类型中的id属性!

    所以,具体的效果是,当尝试调用Integer reg(User user)方法执行插入时,指定了以上配置后,MyBatis会将新插入的数据的id封装到user参数对象中,所以:

    userMapper.reg(user);
    System.out.println(user.getId());
    

    1.2. MyBatis中的动态SQL

    1.2.1. 概念

    MyBatis支持动态SQL,具体的表现是:在配置SQL语句时,可以使用一些简单的逻辑代码,例如if、foreach……使得最终编译出来的SQL语句并不是固定的,而是可能随着参数发生变化的!

    1.2.2. 使用foreach一次删除多条数据

    一次删除多条数据的SQL语句大致是:

    DELETE FROM 表名 WHERE id IN (?,?,?)
    

    而实际应用时,id的列表是由用户进行选取得到的,所以,对于开发者而言,根本就不知道id的列表到底是多少!

    针对这样的需求,应该使用动态SQL中的foreach,遍历id列表,得到SQL语句 中例如(?,?,?)这个部分即可。

    假设在t_user表中实现这样的操作,首先,还是在接口中声明抽象方法:

    Integer delete(List<Integer> ids);
    

    然后,在配置的SQL映射中:

    <delete id="delete">
    	DELETE FROM 
    		t_user 
    	WHERE 
    		id IN (
    			<foreach collection="list" 
    				item="id"
    				separator=",">
    				#{id}
    			</foreach>
    		)
    </delete>
    

    <foreach>节点中,collection表示需要遍历的目标,可以是List集合,也可以是数据,当该节点对应的方法只有1个参数时,该属性的取值是listarray,当该节点对应的方法的参数超过1个时,该属性的取值是参数的名称(是@Param注解中确定的名称);item属性是遍历过程中,取出的数据的名称,是自定义的,在<foreach>节点内部也会使用该名称表示变量;seperator表示分隔符,例如以上删除时,SQL中各个id值应该使用逗号分隔,形成例如1,3,5,7这样的格式,则该属性的值为逗号;还有openclose属性,用于配置整个遍历出来的结果的前缀和后缀,例如在编写SQL语句时没有指定IN关键字后面的括号时,可以添加open="(" close=")"这2项配置。

    1.2.3. 使用if选择性的更新数据

    在有些更新数据的场景里,可能存在:一次性可以更新同一个用户的多项属性数据,但是,如果没有提交其中的某个数据,则该数据不发生变化!例如:在修改个人资料页面中,还可以有“新密码”输入框,如果不想修改密码,则不填写即可。

    针对这样的应用需求,可以添加抽象方法:

    Integer changeInfo(User user);
    

    然后,配置SQL映射:

    UPDATE 
    	t_user 
    SET 
    	<if test="password != null">
    	password=#{password},
    	</if>
    	phone=#{phone},
    	email=#{email},
    	birthday=#{birthday} 
    WHERE 
    	id=#{id}
    

    2. 业务

    2.1. 定位

    在MVC的程序设计中,把整个项目的核心划分出了M、V、C这3大部分。

    其中,V表示View,即视图,通常指的是HTML或JSP文件,用于显示界面,为用户提供操作入口,用户可以在界面进行点击、输入、选择等操作;

    而C表示Controller,即控制器,在原生的JavaEE技术中,指的是Servlet,在SSM框架中,指的是Controller类,主要作用是接收请求,并给予响应,但是,对数据本身并不作实质的处理;

    还有M表示Model,即数据模型,具体的指对数据的处理操作,为了更加明确的划分数据的逻辑与执行的操作,所以,在实际编程时,Model表现为Service和Dao/Mapper的组合,其中,Service用于处理业务,而Dao/Mapper处理数据的增删改查;

    因此,定位为Service的类,通常称之为业务逻辑类,主要用于设计业务的流程与逻辑,即:什么时候允许做什么、先做什么再做什么……

    有了业务逻辑后,更加易于保证数据的安全。

    有了业务逻辑后,持久层(Dao/Mapper)也就不再关心业务,只是单纯的实现增删改查即可,而不用考虑能不能增加或修改或删除等。

    2.2. 用户注册的业务

    2.2.1. 分析业务逻辑

    用户注册中就可以有:用户名必须是唯一的,如果尝试注册的用户名已经被占用,则不允许注册!

    2.2.2. 实现

    首先,持久层(DAO/Mapper)必须具备:根据用户名查询用户信息,以判断某个用户名是否被占用;注册,将用户数据添加到数据表中。

    然后,创建业务接口com.company.mybatis.service.IUserService,并添加抽象方法:

    public interface IUserService {
    
    	User reg(User user);
    
    }
    

    接下来,创建以上接口的实现类com.company.mybatis.service.impl.UserServiceImpl,后续对应的查询、注册功能都需要通过持久层(UserMapper)来完成,所以,在这个类中,需要UserMapper的对象,可以声明为成员变量,然后通过Spring为其自动装配来注入值,当然,要使用自动装配机制,首先得保证整个类(UserServiceImpl)是能够被Spring管理的,所以,当前类还需要添加@Service注解,并且,在Spring的配置文件中开启组件扫描,要能够扫到当前类所在的包!

    然后再实现以上抽象方法:

    @Service("userService")
    public class UserServiceImpl implements IUserService {
    	
    	@Autowired
    	private UserMapper userMapper;
    
    	public User reg(User user) {
    		// 根据user.getUsername()查询用户信息
    		String username = user.getUsername();
    		User result
    			= userMapper.getUserByUsername(username);
    		// 判断查询结果是否为null
    		if (result == null) {
    			// 是:没有被占用,则允许注册
    			userMapper.reg(user);
    			// 返回
    			return user;
    		} else {
    			// 否:已经被占用,则不允许注册
    			throw new RuntimeException(
    				"您尝试注册的用户名(" + username + ")已经被占用!");
    		}
    	}
    
    }
    

    最后,测试:

    public class TestUserService {
    	
    	@Test
    	public void reg() {
    		// 加载Spring的配置文件,获取Spring容器
    		AbstractApplicationContext ac
    			= new ClassPathXmlApplicationContext(
    				"spring-service.xml", "spring-dao.xml");
    		
    		// 获取所需的对象:IUserService
    		IUserService service
    			= ac.getBean("userService", IUserService.class);
    		
    		// 测试执行
    		try {
    			User user = new User("苍教师", "123456");
    			User result = service.reg(user);
    			System.out.println("注册成功:" + result);
    		} catch (RuntimeException e) {
    			System.out.println("注册失败:" + e.getMessage());
    		}
    		
    		// 释放资源
    		ac.close();
    	}
    
    }
    

    2.3. 用户登录的业务

    2.3.1. 分析

    用户需要提交用户名、密码才可以登录,在验证时,应该根据用户名查询用户信息,如果存在,则验证用户输入的密码,与刚才查询结果中的密码是否匹配,如果还能够匹配,则登录成功,如果用户名匹配的数据不存在,或密码不匹配,则登录失败。

    2.3.2. 实现登录验证

    IUserService接口中添加新的抽象方法,表示用户登录:

    User login(String username, String password);
    

    首先,方法名可以是简单易懂的,使用login即可,方法参数取决于用户提交的数据,或必要的数据,则可以是String username, String password,返回值只考虑登录成功后所需要得到的结果,则设计为User类型,后续,当控制器(Controller)调用这个方法,并且成功登录后,可以通过该返回值获取到当前用户的id、用户名甚至头像等等数据,存放到Session中,以用于判断用户已经登录及显示相关数据!

    在设计业务层的方法时,返回值应该使用“操作正确时返回的结果”,而不参考可能出现的业务错误来设计返回值。关于失败的处理,统一使用抛出异常来解决!

    为了保证抛出多种异常能形成程序中的分支,应该为不同错误创建不同的异常类,也就是需要自定义异常,例如:UserNotFoundExceptionPasswordNotMatchException,这些异常都应该存放于业务包的ex子包中,同时,为了便于统一处理异常(至于是仔细处理还是统一处理,都是控制器决定,在写业务层,应该为各种做法都提供基础)还自定义一个ServiceException,作为当前项目中所有的业务层可能抛出的异常的基类,并且是继承自RuntimeException的。

    然后,在UserServiceImpl实现类中实现以上方法:

    public User login(String username, String password) {
    	// 根据用户名查询用户信息
    	// 判断与用户名匹配的用户信息是否存在
    	// 是:存在,判断参数密码和查询到的用户信息中的密码是否匹配
    	// -- 是:匹配,登录成功,将查询到的用户信息作为返回值
    	// -- 否:不匹配,抛出异常:密码错误
    	// 否:不存在,抛出异常:用户名不存在
    }
    

    2.4. 暂时小结

    • 业务逻辑类,主要用于设计业务的流程与逻辑,即:什么时候允许做什么、先做什么再做什么……

    • 在设计业务层的方法时,返回值应该使用“操作正确时返回的结果”,而不参考可能出现的业务错误来设计返回值。关于失败的处理,统一使用抛出异常来解决!

    • 应该创建ServiceException类表示当前项目的业务异常的基类,当前项目中所有的业务异常都应该是这个类的子孙类!

    • 在业务方法的实现中,凡是认定为“错误”或“失败”,均抛出对应的异常对象,并在抛出的对象描述错误信息,如果没有合适的异常类,则创建新的异常类即可!

    其它

    1. 可变参数

    可变参数,表示当调用某个方法时,使用的参数的数量是不确定的,是可能发生变化的,例如:

    public class TestSample {
    
    	public static void main(String[] args) {
    		sum();
    		sum(1);
    		sum(1, 2);
    		sum(1, 2, 3);
    		sum(1, 2, 3, 4);
    		sum(1, 2, 3, 4, 5);
    		sum(1, 2, 3, 4, 5, 6);
    		sum(1, 2, 3, 4, 5, 6, 7);
    	}
    
    	public static void sum(int... numbers) {
    		int result = 0;
    		for (int i = 0; i < numbers.length; i++) {
    			result += numbers[i];
    		}
    		System.out.println(result);
    	}
    
    }
    

    在声明方法时,可变参数使用数据类型... 参数名来表示,例如:

    public static void sum(int... numbers)
    

    在方法体中,处理该参数时,把参数视为数组即可。

    在调用方法时,可变参数的数量最少是0个,即可以不提供任何参数。

    注意:每个方法中最多只允许存在1个可变参数,且可变参数必须是该方法的最后一个参数!

  • 相关阅读:
    bzoj4476: [Jsoi2015]送礼物
    牛客练习赛42 E.热爆了
    bzoj3561: DZY Loves Math VI
    bzoj3560: DZY Loves Math V
    bzoj3512: DZY Loves Math IV
    bzoj3481: DZY Loves Math III
    使用WebUploader本地生成缩略图
    centos 7 安装JDK1.8
    APK反编译
    centos 7 配置pytorch运行环境
  • 原文地址:https://www.cnblogs.com/wood-life/p/10290694.html
Copyright © 2011-2022 走看看