鉴别器discriminator - 按字段选择结果集


更多博客内容访问只爱吃火龙果,点击了解详情


鉴别器和switch语句

无论是在C语言还是Java里,相信大家都会对switch语句有一定的印象,switch语句是多分支选择语句,即根据不同的字段,选择执行不同的语句,最大的用处就是用来处理多分支选择的场景。以前初学C语言的时候,把多选择结构用多个嵌套的if语句来实现,导致代码可读性低,switch语句正好解决这一代码冗长的问题。这篇日志写的鉴别器,discriminator标签,就是类似switch语句,实现根据不同的字段来选择不同的resultMap进行映射。你可能会有疑问,查询之前我们的实体类和结果集映射不都是事先配置好的吗?字段和属性一一对应,为什么还要有选择不同的结果集映射这一步?原因,有可能查询返回多个不同的结果集,或者利用鉴别器discriminator,你可以选择那些字段能被映射,那些字段屏蔽掉不被映射。下面来看看这个鉴别器的使用场景有哪些。

场景 – 限权管理:选择映射有联系的结果集

使用场景之一,就是限权的管理,例如一个商场的数据库,对于不同的顾客,管理员因为权限问题,可以查询得到的顾客信息不同,有的顾客除了基本信息外,只能被查询出省会和城市信息,而有的顾客,除了基本信息外,只能被查询出年龄和生日信息。如果要实现这样的限权管理,可以在查询映射时,使用discriminator标签,鉴别查询得到的每一条顾客信息中,根据数据库中的权限字段,假设权限为“1”的顾客,除了基本信息外,只能被查询出省会和城市;权限为“2”的顾客,除基本信息,只能被查询出生日和年龄。然后鉴别器discriminator在查询返回时,会根据权限字段“1”和“2”,分别给出不同的结果集映射,对于权限为“1”的顾客信息,使用屏蔽生日和年龄的resultMap来映射,而对于权限为“2”的顾客,则使用给出生日和年龄字段的映射关系的resultMap。下面来简单看一下这个实现:

持久化实体类

public class UserDiscriminator1 {
	private int id;
	private String username; //顾客姓名
	private String gender; //性别
	private String email; //电子邮件
	private String province; //省会
	private String city; //城市
	
	public UserDiscriminator1() {
		
	}
	
	public UserDiscriminator1(int id, String username, String gender, String email, 
			String province, String city) {
		this.username = username;
		this.gender = gender;
		this.email = email;
		this.province = province;
		this.city = city;
	}
// 省略get()和set()方法
}

假设我们有两个实体类,分别代表两类顾客,其中一类顾客,UserDiscriminator1,除了基本信息外,我们的限权只能够查询出他们的省会和城市信息。

public class UserDiscriminator2 {
	private int id;
	private String username; //顾客姓名
	private String gender; //性别
	private String email; //电子邮件
	private Date birthday; //生日
	private int age; //年龄
	
	public UserDiscriminator2() {
		
	}
	
	public UserDiscriminator2(int id, String username, String gender, String email, 
			Date birthday, int age) {
		this.username = username;
		this.gender = gender;
		this.email = email;
		this.birthday = birthday;
		this.age = age;
	}
    // 省略get()和set()方法
}

第二类顾客,UserDiscriminator2,除了基本信息,我们的限权只能查出他们的生日和年龄。

在两类顾客的实体类中,假设顾客姓名,性别和电子邮件都是可以查询出来的通用信息,其他的一些个人信息,根据限权的不同,得到的顾客信息会不同。接下来看SQL映射配置文件。

映射配置文件

SQL映射配置文件很重要,因为鉴别器discriminator标签就在里面,它会根据字段的不同,选择不同的case指定的映射方式:

<resultMap type="com.mybatis.po.UserDiscriminator" id="userDiscriminatorResultMap1">
		<id property="id" column="user_id"/>
		<result property="username" column="user_name"/>
		<result property="gender" column="user_gender"/>
		<result property="email" column="user_email"/>
		<discriminator javaType="int" column="user_id">
			<case value="1" resultType="userDiscriminator1">
				<result property="province" column="user_province"/>
				<result property="city" column="user_city"/>
			</case>
			<case value="2" resultType="userDiscriminator2">
				<result property="birthday" column="user_birthday"/>
				<result property="age" column="user_age"/>
			</case>
		</discriminator>
	</resultMap>

可以看到,无论是查询哪一类顾客的信息,他们都共用一个resultMap,里面配置了顾客名,性别以及电子邮件字段的映射配置。接下来,配置我们要根据的字段值,选择不同的case制定的结果集映射。在discriminator标签中,javaType指定了后面比较的字段column的类型,column配置user_id(这是我数据库表中的字段),即根据用户id字段来选择不同的resultMap。在返回的结果集中,每一条记录都会比较它们的用户id,即user_id字段的值,如果值为“1”,则选择相应的case指定的结果集类型UserDiscriminator1(我配置了批量定义别名,所以第一个字母u可以小写)来进行映射,配置了省会province和城市city两个属性的映射关系。如果id为“2”,则选择结果集类型UserDiscriminator2,里面配置了顾客的生日和年龄字段的映射信息。

  在SQL语句中,分别查询两类顾客的通用信息和特殊信息即可:
<select id="queryUserPosition" parameterType="int" resultMap="userDiscriminatorResultMap1">
		select 
			U.id as user_id, 
			U.username as user_name, 
			U.gender as user_gender, 
			U.email as user_email,
			U.province as user_province,
			U.city as user_city
		FROM USER U WHERE U.id=#{id}
	</select>
	
	<select id="queryUserInfo" parameterType="int" resultMap="userDiscriminatorResultMap1">
		select 
			U.id as user_id, 
			U.username as user_name, 
			U.gender as user_gender, 
			U.email as user_email,
			U.birthday as user_birthday,
			U.age as user_age
		FROM USER U WHERE U.id=#{id}
	</select>

配置好resultMap后,在编写测试用例之前,别忘了在全局配置文件中配置这个mapper文件!

测试用例

接下来我们做两个查询,验证一下这个结果集的配置,看鉴别器是否真的根据用户id的1和2选择不同的case指定结果集映射。

@Test
	public void TestDiscriminator1() throws IOException {
		SqlSession sqlSession = dataConn.getSqlSession();
		
		UserDiscriminator1 result1 = sqlSession.selectOne("queryUserPosition", 1);
		System.out.println("姓名:" + result1.getUsername());
		System.out.println("性别:" + result1.getGender());
		System.out.println("电子邮件:" + result1.getEmail());
		System.out.println("所在省会:" + result1.getProvince());
		System.out.println("所在城市:" + result1.getCity());
		
		UserDiscriminator2 result2 = sqlSession.selectOne("queryUserInfo", 2);
		System.out.println("姓名:" + result2.getUsername());
		System.out.println("性别:" + result2.getGender());
		System.out.println("电子邮件:" + result2.getEmail());
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		System.out.println("顾客生日:" + sdf.format(result2.getBirthday()));
		System.out.println("顾客年龄:" + result2.getAge());
		
		sqlSession.close();
	}

在测试用例中我们分别查询id为1和2的顾客,并在结果集中输出顾客的“通用”信息,姓名性别和电子邮件,以及不同结果集映射的信息,如果前面配置正确,我们可以在result1实例中正确获得并输出顾客的省会和城市信息,result2同理:
在这里插入图片描述

可以看到,映射成功,id=“1”对应的结果集类型UserDiscriminator1实体result1中我们映射的是顾客的省会和城市信息。而id=“2”对应的结果集类型UserDiscriminator2实体result2中,我们映射的是顾客的生日和年龄信息,它们都分别成功地输出了。

引用外部resultMap的配置

上面的例子,我们在鉴别器discriminator标签中,我么指定不同case的映射结果集类型resultType,并把不同的映射关系result标签配置在不同的case标签里。我们也可以引用外部的resultMap,像配置association标签和collection标签那样:

<!-- 引用外部resultMap配置 -->
	<resultMap type="com.mybatis.po.UserDiscriminator" id="userDiscriminatorResultMap2">
		<id property="id" column="user_id"/>
		<result property="username" column="user_name"/>
		<result property="gender" column="user_gender"/>
		<result property="email" column="user_email"/>
		<discriminator javaType="int" column="user_id">
			<case value="1" resultMap="userResultMap1"/>
			<case value="2" resultMap="userResultMap2"/>
		</discriminator>
	</resultMap>

case标签中,我们直接指定使用外部的结果集resultMap来进行映射,不用在标签里指定结果集的实体类型,并一个一个地配置。

外部的resultMap:

<resultMap id="userResultMap1" type="com.mybatis.po.UserDiscriminator1"/>
		<result property="province" column="user_province"/>
		<result property="city" column="user_city"/>
	</resultMap>
	
	<resultMap id="userResultMap2" type="com.mybatis.po.UserDiscriminator2"/>
		<result property="birthday" column="user_birthday"/>
		<result property="age" column="user_age"/>
	</resultMap>

可以说是把这些一个一个的字段和属性映射配置在了外面。不过这种配置方法,有一点要注意的是,和C或Java中的switch语句有点不同(除非在switch中每一个case最后都加上break),鉴别器discriminator一旦选择了一个结果集映射后,其余不符合的case指定的结果集(指外部的resultMap)都会被忽略掉!看回上面的配置:

<!-- 引用外部resultMap配置 -->
	<resultMap type="com.mybatis.po.UserDiscriminator" id="userDiscriminatorResultMap2">
		<id property="id" column="user_id"/>
		<result property="username" column="user_name"/>
		<result property="gender" column="user_gender"/>
		<result property="email" column="user_email"/>
		<discriminator javaType="int" column="user_id">
			<case value="1" resultMap="userResultMap1"/>
			<case value="2" resultMap="userResultMap2"/>
		</discriminator>
	</resultMap>

也就是说,虽然我们一开始制定了username,gender以及email的映射关系,但是在discriminator中,如果选中了其中一个case指定的外部resultMap,那么在结果集映射时就会使用这个外部的resultMap,其他的结果映射都会被忽略,包括上面的三个通用配置。这是要注意的一点,使用的测试用例和上面的一样,关键是看看它的输出结果:

@Test
	public void TestDiscriminator1() throws IOException {
		SqlSession sqlSession = dataConn.getSqlSession();
		
		UserDiscriminator1 result1 = sqlSession.selectOne("queryUserPosition", 1);
		System.out.println("姓名:" + result1.getUsername());
		System.out.println("性别:" + result1.getGender());
		System.out.println("电子邮件:" + result1.getEmail());
		System.out.println("所在省会:" + result1.getProvince());
		System.out.println("所在城市:" + result1.getCity());
		
		UserDiscriminator2 result2 = sqlSession.selectOne("queryUserInfo", 2);
		System.out.println("姓名:" + result2.getUsername());
		System.out.println("性别:" + result2.getGender());
		System.out.println("电子邮件:" + result2.getEmail());
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		System.out.println("顾客生日:" + sdf.format(result2.getBirthday()));
		System.out.println("顾客年龄:" + result2.getAge());
		
		sqlSession.close();
	}

使用上面的映射配置,分别做查询,result1对象选中case value=“1”的结果集userResultMap1来映射,可以得到省会和城市信息。Result2对象选中 case value=“2”的结果集userResultMap2来映射,可以得到生日和年龄信息。这两个结果集都是引用外部的,接下来看看运行结果:

可以看到,两个对象输出中,三个通用信息都是null,原因并不是数据为空,而是它们没有得到映射关系,property不知道和哪一个column对应。

那则么办?解决的方法还是有的,如果你想让鉴别器discriminator匹配了结果集后,还能使用上面resultMap的映射关系,可以让这两个外部的resultMap,extends继承鉴别器上面的,含有顾客名,性别和电子邮件映射关系的resultMap:

<resultMap id="userResultMap1" type="com.mybatis.po.UserDiscriminator1" 
		extends="userDiscriminatorResultMap2">
		<result property="province" column="user_province"/>
		<result property="city" column="user_city"/>
	</resultMap>
	
	<resultMap id="userResultMap2" type="com.mybatis.po.UserDiscriminator2" 
		extends="userDiscriminatorResultMap2">
		<result property="birthday" column="user_birthday"/>
		<result property="age" column="user_age"/>
	</resultMap>

这样就可以保留结果集userDiscriminatorResultMap2中的配置映射关系。

级联(嵌套)中的discriminator配置

在关联的嵌套结果集中,我们也可以配置鉴别器,还是上面的例子,例如我们有这样一个实体类:

public class UserDiscriminator {
	private int id;
	private String username; //顾客姓名
	private String gender; //性别
	private String email; //电子邮件
	HashMap<String, String> userInfo = new HashMap<String, String>();
 
	public UserDiscriminator() {
		
	}
	
	public UserDiscriminator(int id, String username, String gender, 
			String email, HashMap userInfo) {
		this.id = id;
		this.username = username;
		this.gender = gender;
		this.email = email;
		this.userInfo = userInfo;
	}
    // 省略get()和set()方法
}

里面嵌套了一个HashMap,用来存放不同的映射结果信息,这样我们就不用创建两个实体类来分别存储两个不同映射结果了。具体来看它们的映射配置:

<!-- 级联,关联的嵌套结果集中配置discriminator -->
	<resultMap type="com.mybatis.po.UserDiscriminator" id="userDiscriminatorResultMap3">
		<id property="id" column="user_id"/>
		<result property="username" column="user_name"/>
		<result property="gender" column="user_gender"/>
		<result property="email" column="user_email"/>
		<association property="userInfo" javaType="java.util.HashMap">
			<discriminator javaType="int" column="user_id">
				<case value="1" resultMap="userResultMap3"/>
				<case value="2" resultMap="userResultMap4"/>
			</discriminator>
		</association>
	</resultMap>

Association标签配置嵌套结果集,javaType是HashMap。discriminator中引用的也是外部的resultMap,这两个resultMap和前面配置的外部resultMap,除了结果集类型不同外,其他配置一样:

<resultMap id="userResultMap3" type="java.util.HashMap">
		<result property="province" column="user_province"/>
		<result property="city" column="user_city"/>
	</resultMap>
	
	<resultMap id="userResultMap4" type="java.util.HashMap">
		<result property="birthday" column="user_birthday"/>
		<result property="age" column="user_age"/>
	</resultMap>

最后来看测试用例的代码:

@Test
	public void TestDiscriminator2() throws IOException {
		SqlSession sqlSession = dataConn.getSqlSession();
		UserDiscriminator result1 = sqlSession.selectOne("queryUserPosition", 1);
		HashMap<String, String> resultMap = result1.getUserInfo();
		System.out.println("姓名:" + result1.getUsername());
		System.out.println("性别:" + result1.getGender());
		System.out.println("电子邮件:" + result1.getEmail());
		System.out.println("所在省会:" + resultMap.get("province"));
		System.out.println("所在城市:" + resultMap.get("city"));
		resultMap.clear();
		
		UserDiscriminator result2 = sqlSession.selectOne("queryUserInfo", 2);
		resultMap = result2.getUserInfo();
		System.out.println("姓名:" + result2.getUsername());
		System.out.println("性别:" + result2.getGender());
		System.out.println("电子邮件:" + result2.getEmail());
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		System.out.println("顾客生日:" + sdf.format(resultMap.get("birthday")));
		System.out.println("顾客年龄:" + resultMap.get("age"));
		sqlSession.close();
	}

测试鉴别器选择不同的结果集映射,得到的结果都可以用同一个实例对象来接收,因为不同映射的结果类型都是HashMap接收。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 鉴别器Discriminator)是深度学习中的一个重要组件,用于判别输入数据的真实性。在GAN(Generative Adversarial Networks)中,鉴别器被用来判别生成器生成的数据是否为真实数据,从而指导生成器的训练。在PyTorch中,可以使用以下代码实现一个简单的鉴别器: ```python import torch.nn as nn class Discriminator(nn.Module): def __init__(self): super(Discriminator, self).__init__() self.model = nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, output_dim), nn.Sigmoid() ) def forward(self, x): x = self.model(x) return x ``` 其中,`input_dim`表示输入数据的维度,`hidden_dim`表示隐层的维度,`output_dim`表示输出数据的维度。在`forward`方法中,我们对输入数据进行前向传播,并返回鉴别器的输出结果。在训练过程中,我们将真实数据和生成器生成的数据输入到鉴别器中,计算损失并进行反向传播更新模型参数。 ### 回答2: 在PyTorch中,鉴别器discriminator)是一种重要的神经网络模型,通常用于生成对抗网络(GAN)中。GAN是一种机器学习模型,由生成器(generator)和鉴别器两个子模型组成。 鉴别器的任务是判断给定的输入数据是真实数据还是由生成器生成的合成数据。它的设计和训练旨在最大限度地提高对真实数据的识别能力,并尽量将生成器生成的数据判定为合成数据。 在PyTorch中,鉴别器通常由多个卷积层和全连接层组成。通过卷积操作,鉴别器可以对输入数据进行多次特征提取,使其具备较强的表征能力。全连接层则用于将提取到的特征映射到最终的输出结果,通常通过sigmoid函数将该结果限制在0到1之间,以表示输入数据是真实数据的概率。 在训练过程中,鉴别器的目标是最小化真实数据和合成数据之间的差异,通过梯度下降法逐渐优化其权重和参数。而生成器的目标则是尽可能欺骗鉴别器,使其无法准确判断输入数据的真伪。 通过生成器和鉴别器之间的对抗训练,GAN能够逐渐学习到生成真实数据的能力,从而生成逼真的合成数据。这使得GAN在许多领域(如图像生成、文本生成等)中有着广泛的应用。在PyTorch中,我们可以使用已有的库和工具,如torch.nn模块和torch.optim模块来构建和训练鉴别器模型,从而实现各种各样的生成任务。 ### 回答3: discriminator 鉴别器是机器学习中的一个重要概念,用于判断输入数据的真实性或者类别。在深度学习中,尤其是生成对抗网络(GANs)中,鉴别器被用于判断生成器生成的样本是真实的还是伪造的。 在PyTorch中,我们可以很方便地实现一个discriminator 鉴别器。首先,我们需要定义一个神经网络模型来充当鉴别器。这个模型可以是一个多层感知机(MLP)或者卷积神经网络(CNN),具体选择取决于应用场景。 接下来,我们需要定义鉴别器的前向传播函数。前向传播函数的目标是将输入数据传递到模型中,并输出一个标量值,用于表示输入数据的真实性或者类别。在GANs中,鉴别器的输出可以是一个介于0到1之间的概率值,表示输入数据属于真实样本的概率。通常,我们会使用Sigmoid函数将输出值映射到0到1的范围内。 然后,我们需要定义鉴别器的损失函数。在生成对抗网络中,鉴别器的目标是最小化真实样本的损失并最大化伪造样本的损失。一种常用的损失函数是二元交叉熵损失(Binary Cross Entropy Loss),它可以帮助我们衡量鉴别器的性能。 最后,我们需要使用优化算法对鉴别器进行训练。常见的优化算法有随机梯度下降(SGD)和Adam等。通过调整优化算法的超参数,我们可以使鉴别器逐渐提高对真实样本和伪造样本的鉴别能力。 总而言之,通过PyTorch提供的灵活性和强大的计算能力,我们可以轻松地实现一个discriminator 鉴别器,并用于各种应用中,如图像生成、文本生成等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值