ef 更新未找到源文件info记录,无法更新

  本文将简要介绍Sentinel SuperPro软件加密锁的编程开发知识,可供需要进行软件加密锁开发的软件开发商快速学习掌握这种类型的加密锁的编程开发,快速保护自己的软件不受侵害,防止加密狗被破解。本文资源来源于Sentinel SuperPro加密锁开发套件中的开发手册,仅供大家学习,文字版权属于Sentinel SuperPro所有。  一、加密锁简介  Sentinel SuperPro加密锁是一个硬件/软件保护系统,功能主要是保护软件,即防止未经允许非法使用开发商的软件。如未把正确的加密锁与计算机连接,则被保护的应用程序将无法实现全部功能。因此,只有合法用户才能使用开发商的产品。  使用SuperPro软件保护锁完成软件保护,就是将软件开发商的未被保护的应用程序与SuperPro软件保护锁加密算法相绑定过程。最终实现绑定后的应用程序没有软件保护锁不能单独运行,只有插入软件开发商自己定义算法的软件保护锁,应用程序才能正常运行。当软件开发商的软件销售给最终用户后,软件即使被非法复制,而没有软件开发商提供的软件保护锁软件不能被使用。这样一来,从技术上防止盗版起到软件保护的作用。  应用程序和Sentinel Superpro软件保护锁加密算法的绑定,是通过API函数调用来校验软件保护锁是否存在来实现的。API函数验证分由三部分组成:  1)询问部分:开发商的应用程序通过API调用向软件保护锁的驱动程序发出的&查询串&, 驱动程序自动将&查询串&传给并口或USB口上的SuperPro软件保护锁上的算法单元。  2)运算部分:加密锁通过内部的算法芯片计算&查询串&,并将运算结果&响应串&返回给驱动程序。  3)程序鉴定、行动部分:驱动程序将&响应串&返回给应用程序中的API函数调用。应用程序对返回值&&响应串&进行比较判断,判断与预知运算结果是否相等,根据判断的结果进行相应的行动。  如果返回值&&响应串&与预知运算结果相等,则说明保护应用程序的软件保护锁存在,使用程序的用户为合法用户。不相等则说明保护应用程序的软件保护锁不存在,用户为非法用户,应用程序将终止被使用。  SentinelSuperPro 系统一个主要优点是可对一个加密锁编程,以提供多种驱动程序类型,包括固定响应和可变响应。这样,所创建的软件锁具有非常多的类型。  例如,可用单元存储固定数据(如序列号、用户名称)或存储控制功能询问的代码。然后,可简单地读取这些数据,以检验是否附加了加密锁。也可使用存储的数据控制程序流程或应用程序的函数。数据字组可以定义为只读的(锁定)或可读/写的。  每个加密锁的前 8 个单元为系统信息保留。除了某些限制外,可以任意方式使用其它 56 个单元。  二、加密锁编程接口  下面以VB语言为例,说明使用SuperPro软件保护锁时,有关API调用的数据结构、函数以及必要的步骤。  1.基本信息  DEVELOPER ID:用户ID为加密编号,为固定数字,没有它就不能使用加密锁。  地址:SuperPro 的地址从00H到3FH,以十六进制数表示。  访问权限:SuperPro的每一个存储单元可以单独设置成不同的操作权限。  1- 可读/写数据;2- 只读数据;3- 计数器;4- 算法  2.数据结构  APIPACKET  Type APIPACKET    data(4096) As Byte  End Type  除了sproInitialize, 所有的函数都使用APIPACKET数据结构,该结构在调用sproFindFirstUnit 时被初始化,在调用sproFindNextUnit的时候会根据下一个加密锁的信息进行必要修改。当函数正常返回时,APIPACKET中存放必要的返回信息。  SuperPro驱动程序使用该结构中的信息与加密锁进行通讯。开发者不要自行修改其中的数据。SuperPro驱动程序不负责分配APIPACKET数据单元,开发者在程序中必须分配APIPACKET结构变量,并且把指针传递给不同的函数。  3.API函数  ● RNBOsproFindFirstUnit()  RNBOsproFindFirstUnit() 函数定位具有指定的开发者ID的SentinelSuperPro 加密锁。在调用 RNBOsproInitialize() 之后,使用其他调用之前,必须首先调用该函数。如果找到了加密锁,讯息包或UNITINFO 记录中将包含有效的数据,如果未找到加密锁,讯息包或 UNITINFO 记录将被标记为无效的。  RNBOsproFindFirstUnit() 将搜索所有连接在并行端口上的所有级联单位。如果存在多个加密锁具有相同的开发者 ID,RNBOsproFindFirstUnit() 存取找到的第一个加密锁。在需要的时候,可以使用 RNBOsproFindNextUnit()查找具有相同开发者 ID 的其他加密锁。  参数  RNBOsproFindFirstUnit() 调用需要以下参数:  ■由 Rainbow Technologies 或者其他发布者为您指定的开发者 ID。  ■指向讯息包记录或者 UNITINFO 结构的指针。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  ● RNBOsproFindNextUnit()  RNBOsproFindNextUnit() 函数搜索具有给定开发者 ID 的下一个加密锁。  开发者ID是由第一次调用的 RNBOsproFindFirstUnit() 指定的。(要找到具有不同的开发者ID的加密锁,需要再 次调用RNBOsproFindFirstUnit()。)  如果 RNBOsproFindNextUnit() 成功了,讯息包或者 UNITINFO 记录将包含下一个加密锁的数据。如果未成功,讯息包或者 UNITINFO 记录将被标记为无效的。要重新对讯息包或 UNITINFO 记录初始化,需要再次使用 RNBOsproFindFirstUnit() 以及 RNBOsproFindNextUnit()。  RNBOsproFindNextUnit() 将搜索连接到任何并行端口上的所有级联单位。如果同时连接了几个加密锁,应用程序可 以多次调用RNBOsproFindNextUnit()。  参数  RNBOsproFindNextUnit()函数需要一个参数:指向讯息包记录或者UNITINFO 结构的指针。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  ● RNBOsproFormatPacket()  RNBOsproFormatPacket() 函数检验讯息包记录大小的有效性,并且初始化它的缺省值。该调用必须在调用其他 API 函数之前进行,否则将返回错误码 2 (INVALID PACKET)。  参数  RNBOsproFormatPacket() 需要下列参数:  ■指向讯息包记录 (RBP_RNBOspro_APIPACKET) 的指针。  ■PacketLen 的值是一个整数, 包含了讯息包的长度 (1028 个字节) 。  返回值  所有的函数都返回一个无符号的 16 位的值。值为 0 表示操作成功了,其他的值都表明发生了错误。如果发生了错误,函数将返回附录中列出的状态码之一。如果返回的状态码为非 0 的值,那么函数返回的其他数据将是无意义的。  ● RNBOsproGetVersion()  RNBOsproGetVersion() 函数返回与 SentinelSuperPro 驱动程序有关的信息,应用程序可以使用该信息。  参数  RNBOsproGetVersion() 函数需要下列参数:  ■指向讯息包记录或者 UNITINFO 结构的指针。  ■指向用来返回主版本号的位置的指针。  ■指向用来返回次版本号的位置的指针。  ■指向用来返回修订版本号的位置的指针。  ■指向用来返回驱动程序类型标识符的位置的指针。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  ● RNBOsproInitialize()  RNBOsproInitialize() 函数使驱动程序执行所需的初始化操作。在调用任何其他的 API 之前,应用程序必须首先调用 RNBOsproInitialize() 一次。  参数  RNBOsproInitialize() 函数不需要任何参数。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  ● RNBOsproOverwrite()  RNBOsproOverwrite() 函数使应用程序能够改变字组的值与存取码,但是被限制的单元(0 到 7)除外。在使用该函数的时候,需要同时提供写密码和重写密码。  注意重写密码的能力是相当强大的。它的使用应该仅限于工作室中,而不应该在正式发布的应用程序中出现。如果您的保护方案不需要在应用程序中使用重写密码,Rainbow 可以通过编程使您的每一个加密锁具有一个&随机重写密码&。每个加密锁中将包含不同的重写加密锁密码,并且不能在最终用户实际使用中进行修改。  参数  RNBOsproOverwrite() 函数需要下列参数:  ■指向讯息包记录或者 UNITINFO 结构的指针。  ■您的写密码。  ■您的两个字组长的重写密码。  ■需要写入的定址。  ■赋予该字组的存取码:0 (可读/写数据)、1 (只读数据)、2 (计数器)、3 (算法/隐藏的)。  ■希望写到该定址上的值。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  ● RNBOsproQuery()  利用RNBOsproQuery() 函数,应用程序可以发送询问字串到SentinelSuperPro 加密锁。应用程序还可以指定用来对字串进行转换的算法描述符。  加密锁将使用自己的算法和指定的算法描述符中储存的信息对输入字串进行转换。然后加密锁将转换之后的字串返回到应用程序。应用程序将返回的字串与希望得到的字串相比较,即可知道所需要的加密锁是否仍连接在并行端口上。  在软件开发阶段,高级编程序的鉴定 API:询问选项可以用来确认加密锁的算法描述符对给定的输入字串进行转换的结果。  参数  RNBOsproQuery() 函数需要下列参数:  ■指向讯息包记录或者 UNITINFO 结构的指针。  ■询问字串中的字节个数(最大值 56)。  ■指向包含询问字串的缓冲区的指针(偏移量在前,段定址在后)。  ■指向用来供驱动程序返回结果字串的缓冲区的指针。该缓冲区需要足够大,以便容纳整个的结果字串。另外一种可取的办法是,将某个位置的指针传递给驱动程序,但驱动程序仅返回结果字串的最后 32 个位。  ■询问中使用的算法描述符的首(较低的)字组的定址。该定址必须是偶数。一般来说,较长的询问字串能够提供更好的保护。推荐的询问字串长度至少为 8 个十六进制字符(32 个位)。驱动程序允许的最长的询问字串的长度为 56 个字节。如果发送的字串长度小于 32 位,驱动程序将在添加一种已知的模式之后再将其发送出去。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  ● RNBOsproRead()  应用程序可以使用 RNBOsproRead() 函数读取非隐藏的记忆体字组的值。非隐藏的字组的存取码为 0 (可读/写数据)、1 (只读数据)或 2(计数器)。算法/隐藏的字组的存取码为 3,它是不能被读取的。如果试图读取一个隐藏的字组,驱动程序将返回拒绝存取。通过检查该状态即可判断字组是不是算法字组。RNBOsproRead() 返回字组的值,而不是其存取码。要获得字组的存取码,可以使用 RNBOsproExtendedRead()。  参数  RNBOsproRead() 函数需要下列参数:  ■指向讯息包记录或者 UNITINFO 结构的指针。  ■被读取的定址。  ■一个指针,在调用成功的时候,该指针指向驱动程序返回的指定字组的内容。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  ● RNBOsproDecrement()  RNBOsproDecrement() 函数将可读/写的数据字组(存取码为 0)或者计数器字组(存取码为 2)的值减量 1。在使用该函数时需要提供写密码。如果试图对被锁定或者隐藏的字组减量,驱动程序将返回拒绝存取。如果字组中的值已归零,驱动程序将返回已归零。对这两种状态码应用程序都需要进行检查。RNBOsproDecrement() 可以用来限制演示程序能够被执行的次数。首先将一个计数器与一个算法描述符联系起来,然后在每次程序执行的时候将计数器减一。当计数器为 0 时,算法将自动被去激活。以后的询问将返回无效的响应。  参数  RNBOsproDecrement() 函数需要下列参数:  ■指向讯息包记录或者 UNITINFO 结构的指针。  ■您的写密码。  ■需要被减量的数据字组或计数器的定址。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  ● RNBOsproExtendedRead()  RNBOsproExtendedRead() 函数读取非隐藏的记忆体字组的值和存取码。未隐藏的字组的存取码为 0 (可读/写数据)、(只读数据)或 2 (计数器)。算法/隐藏字组的存取码为 3,它们是不可读的。如果试图读取一个隐藏的字组,驱动程序返回拒绝存取。通过检查该状态,能够确认一个字组是否是算法字组。  参数  RNBOsproExtendedRead() 函数需要下列参数:  ■指向讯息包记录或者 UNITINFO 结构的指针。  ■要读取的定址。  ■指向指定字组的内容的指针,在调用成功时由驱动程序返回内容。  ■指向指定字组的存取码的指针,在调用成功时由驱动程序返回存取码。存取码可能是 0 (可读/写数据)、1 (只读数据)或 2 (计数器)。调用不可能返回存取码 3 (算法/隐藏的)。  返回值  如果执行成功,函数返回成功。如果发生了错误,函数将返回附录中所列的状态码。  4.必要的初始化步骤  在调用API函数进行加密工作以前,必须在应用程序中完成以下有关加密的初始化工作。  分配APIPACKET数据结构  调用RNBOsproFormatPacket初始化结构(仅Win32程序使用)  调用RNBOsproInitialize函数  调用RNBOsproFindFirstUnit函数找到软件保护锁  以上工作完成以后,开发者可以根据各自的需要在程序的任何部分进行读写、查询等工作。所有函数没有必然的前后顺序。  当利用查询响应对,调用RNBOSproQuery进行查询时,需要对查询响应对进行适当转换。  三、备注  如果想了解更多有关软件保护锁的信息,请参阅加密锁手册SentinelSuperPro Developer Guide。  附录.API状态码返回数值  0 success - 调用顺利地完成。  1 invalid function code - 指定了一个无效功能码。对于有效的 API 功能码,请查看您所使用的语言的包含档案(例如:SUPERPRO.H )。如果使用 Rainbow 提供的接口程序与驱动程序通讯,通常不应该产生该错误。  2 invalid packet - 在命令讯息包中检测到核对和错误,该错误表明内部不一致。因是讯息包记录或 UNITINFO 结构还没有被初始化,或者可能已被篡改了。如果使用Rainbow 提供的接口程序与驱动程序通讯,通常不应该产生该错误。  3 unit not found - 或者 RNBOsproFindFirstUnit(),或者 RNBOsproFindNextUnit() 无法找到指定的 SentinelSuperPro 加密锁。请确信发送了正确的开发者 ID。如果该加密锁消失(也就是说,已?被取消),该错误是其它函数返回的。  4 access denied - 企图对字组执行非法操作。例如,您可能试图从算法/隐藏字组中读取、向锁定的字组中写入,或者将不是计数器字组或者数据字组的字组减量。  5 invalid memory address - 指定了无效的 SentinelSuperPro 记忆体定址。有效的定址是十进制的 0-63(十六进制的 0-3F)。对于许多操作,单元 0-7 是无效的。必须使用第一个(偶数)定址引用算法描述符。  6 invalid access code - 指定了一个无效的存取码。存取码必须是 0(读/写数据)、1(只读数据)、2 (计数器)或者 3(算法/隐藏)。  7 port is busy - 因为端口被占用,所以请求的操作不能完成。导致的原因可能是有大量的打印任务,或者该端口上的某个单位正在执行写操作并阻塞了该端口。请再试一次该函数。  8 write not ready - 由于暂时缺少足够的能力,不能执行写入或者减量操作。请再试一次该操作。  9 no port installed - 在工作站上没有发现并行端口。  10 already zero - 试图将已?包含 0 的计数器字组或者数据字组减量。如果使用该计数器控制演示程序执行,当对应的算法描述符被起动密码重新激活之后,可能会出现这种情况。  12 driver not installed - 没有安装或者没有检测到系统设备驱动程序。不可能与该单位通讯。请验证设备驱动程序的加载是否正确。  13 communications error - 系统设备驱动程序与该单位通讯时碰到问题。请验证设备驱动程序的安装是否正确。  18 version not supported - 当前的系统设备驱动程序过期。请更新驱动程序。  19 OS environment not supported - 客户程序库不支持该操作系统或者环境。请与技术支持联系。  20 query too long - 发送的询问字串超过 56 个字符。请发送较短的字串。  30 driver is busy - 系统驱动程序繁忙。请再试一次该操作。  31 port allocation failure - 通过操作系统的并行端口争用处理器来分配并行端口时失败。  32 port release failure - 通过操作系统的并行端口争用处理器来释放先前分配的并行端口时失败。  39 acquire port timeout - 在规定的超时时间内请求使用并行端口失败。  42 signal not supported - 特定的机器不支持信号线。例如,企图在一台 NEC 9800 计算机上使用 ACK 线。  57 init not called - 没有初始化加密锁。在调用产生该错误的函数之前,请先调用RNBOsproInitialize() 函数。  58 driver type not supported - 对于定义的操作系统和客户程序库,不支持驱动程序的存取类型。该驱动程序的存取类型无论是直接 I/O,还是系统驱动程序。  59 fail on driver comm - 客户程序库与 Rainbow 的系统驱动程序通讯失败。  60 API status unavailable - 扩充的 API 状态函数不可用。  255 invalid status - 返回的状态码无效。未找到info记录,无法更新_百度知道
未找到info记录,无法更新
什么意思?
我有更好的答案
把机子带的光盘上98驱动装上,用驱动再连机子,然后再格式化
采纳率:72%
为您推荐:
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。Hibernate单、双向关联与inverse属性
关键字: Hibernate基础知识
6.2 问题:Hibernate单、双向关联与inverse属性 6.2.1 Hibernate的单、双向关联http://book.csdn.net/bookfiles/563/.shtml 设计师L并不理解在Hibernate中单向关联与双向关联有什么区别。于是他也就无法告诉开发人员,在配置实体间关系时究竟如何配置。从Hibernate的文档中来看,官方并没有特别告诉使用者哪种关联方式可靠。而且从L的实验结果来看,通常的一些行为使用单向关联与双向关联的结果一样。那么究竟该如何看待这两种关联方式呢? 6.2.2 Hibernate的单向关联常规实现 依旧从实体房间(Room)与实体人(UserInfo)的一对多关联来看(这里将使用基于外键的一对多,对于连接表将放在另一个论题中),其两个实体如图6.2所示。 图6.2 两个实体的类图 实体房间(Room)与实体人(UserInfo)的代码实现见例6.8。 例6.8:实体房间(Room.java)与实体人(UserInfo.java) Room.java public class Room { //房间实体的主键对应
//房间号 private S //房间名称 private S //房间中的人(这是一个Set类型的集合) private S //get/set方法 … } UserInfo.java public class UserInfo { //实体人的主键
//人的名字 private S //人的性别 private S //实体房间的主键
//get/set方法 … } 针对实体房间(Room)与实体人(UserInfo)的代码以及数据库表的单向一对多,其Hibernate映射文件见例6.9。 例6.9:实体房间(Room.java)与实体人(UserInfo.java)的映射文件 Room.hbm.xml &?xml version='1.0' encoding='UTF-8'?& &!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http:// hibernate. sourceforge.net/hibernate-mapping-3.0.dtd"& &!-- Room实体的package --& &hibernate-mapping package="testhibernate"& &!-- Room实体的class和表 --& &class name="Room" table="room"& &!-- 映射id主键 --& &id name="id" column="id" type="java.lang.Long"& &!-- 映射为自增长类型 --& &generator class="increment" /& &/id& &!-- 映射name属性 --& &property name="name" column="NAME" type="java.lang.String" /& &!-- 映射roomnumber属性 --& &property name="roomnumber" column="roomnumber" type="java.lang.String" /& &!-- 映射一对多 --& &!-- 通过Room实体的users集合属性映射,级联动作为全部 --& &set name="users" cascade="all"& &!-- 映射User表的外键roomid --& &key column="roomid"&&/key& &!-- 一对多映射class UserInfo --& &one-to-many class="UserInfo"&&/one-to-many& &/set& &/class& &/hibernate-mapping& UserInfo.hbm.xml &?xml version='1.0' encoding='UTF-8'?& &!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/HibernateMappingDTD3.0//EN""http://hibernate.sourceforge. net/hibernate-mapping-3.0.dtd"& &!-- UserInfo实体的package --& &hibernate-mapping package="testhibernate"& &!-- UserInfo实体的class和表 --& &class name="UserInfo" table="userinfo"& &!-- 映射id主键 --& &id name="id" column="id" type="java.lang.Long"& &!-- 映射为自增长类型 --& &generator class="increment" /& &/id& &!-- 映射name属性 --& &property name="name" column="NAME" type="java.lang.String" not-null ="true" /& &!-- 映射roomnumber属性 --& &property name="sex" column="SEX" type="java.lang.String"/& &!-- &many-to-one name="room" column="roomid" class="Room" /&--& &!-- 映射roomid属性作为UserInfo实体与Room实体的关联,其字段在userinfo表中 --& &property name="roomid" column="roomid" type="java.lang.Long"/& &/class& &/hibernate-mapping& 6.2.3 单向关联的实现和问题 下面针对当前Hibernate映射文件给出了具体实现。为了代码完整,先给出Hibernate的配置文件,见例6.10。 例6.10:hibernate.cfg.xml &?xml version='1.0' encoding='UTF-8'?& &!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"& &!-- Generated by MyEclipse Hibernate Tools. --& &hibernate-configuration& &session-factory& &!-- 数据库配置和映射文件配置 --& &property name="connection.username"&XXXX&/property& &property name="connection.url"&jdbc:oracle:thin:@XXX:1521:XXX&/property& &property name="dialect"&org.hibernate.dialect.Oracle9Dialect&/property& &property name="connection.password"& XXXX &/property& &propertyname="connection.driver_class"&oracle.jdbc.driver. OracleDriver&/property& &!-- 显示SQL语句 --& &property name="show_sql"&true&/property& &!-- 显示格式化的SQL语句 --& &property name="format_sql"&true&/property& &!-- 显示哪个方法中执行的SQL语句 --& &property name="use_sql_comments"&true&/property& &!-- &mapping resource="Licencedata.hbm.xml" /& --& &!-- 两个映射文件 --& &mapping resource="UserInfo.hbm.xml" /& &mapping resource="Room.hbm.xml" /& &/session-factory& &/hibernate-configuration& 接着给出的是Hibernate与数据库交互的测试代码,见例6.11。 例6.11:执行插入表 public void run() { //创建Room实体 Room room = new Room(); //设置Room.name room.setName("rwhome"); //设置Room.roomnumber room.setRoomnumber("001"); //创建UserInfo实体 UserInfo userInfo = new UserInfo(); //设置UserInfo.name userInfo.setName("rw"); //设置UserInfo.sex userInfo.setSex("M"); //创建UserInfo集合userInfoSet Set userInfoSet = new HashSet(); //添加UserInfo实体到集合userInfoSet userInfoSet.add(userInfo); //设置Room.users(这是一个集合类型) room.setUsers(userInfoSet); //创建Hibernate Session Session session = HibernateSessionFactory.currentSession(); //启动事务 Transaction tx = session.beginTransaction(); //持久化Room实体 session.save(room); session.flush(); //打印出持久化状态的各实体内容 System.out.println("RoomId:" + room.getId()); System.out.println("Name:" + room.getName()); System.out.println("Roomnumber:" + room.getRoomnumber()); Iterator it = room.getUsers().iterator(); while (it.hasNext()) { UserInfo userInfoin = (UserInfo)it.next(); System.out.println("UserId:" + userInfoin.getId()); System.out.println("Name:" + userInfoin.getName()); System.out.println("Roomid:" + userInfoin.getRoomid()); System.out.println("Sex:" + userInfoin.getSex()); } //提交事务 tx.commit(); //关闭Hibernate Session HibernateSessionFactory.closeSession(); } 由于在Hibernate映射文件中配置了级联(cascade="all"),因此只需要对Room实体进行持久化操作,会关联持久化UserInfo实体。 看起来现在一切都完好了,可惜这里却有一个小小的缺陷。这段代码在不同的前提下会生成两种结果。前提就是:room表与userinfo表是否存在外键关联。 (1)当room表与userinfo表未曾设置外键关联时,那么这段代码就是正确的,其执行后打印出来的SQL语句如下: Hibernate: //对room表自增长字段(主键)的最大值获取 select max(id) from room Hibernate: //对userinfo表自增长字段(主键)的最大值获取 select max(id) from userinfo Hibernate: /* 插入room表 */ insert into room (NAME, roomnumber, id) values (?, ?, ?) Hibernate: /* 插入userinfo表,此时roomid字段为0 */ insert into userinfo (NAME, SEX, roomid, id) values (?, ?, ?, ?) Hibernate: /* 通过一对多的关联映射更新userinfo表中当前记录的roomid字段 实现room表与userinfo表的关联 */ update userinfo set roomid=? where id=? 关于这段SQL的执行过程见注释。可以看到,Hibernate在处理一对多单向关联时,是通过三句SQL来完成的。首先是插入主表(room表),然后是插入子表(userinfo表),最后更新子表的关联字段(userinfo表的roomid字段)为主表的主键(room表的id字段)。 (2)当room表与userinfo表设置外键关联时,那么这段代码就是错误的,其执行后打印出来的SQL语句如下: Hibernate: /*插入room表*/ insert into room (NAME, roomnumber, id) values (?, ?, ?) Hibernate: /*插入userinfo表,此时roomid字段为0*/ insert into userinfo (NAME, SEX, roomid, id) values (?, ?, ?, ?)
13:47:28,822 WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 2291, SQLState: 23000
13:47:28,822 ERROR [org.hibernate.util.JDBCExceptionReporter] - ORA-02291: 违反完整约束条件 (TEST.FOREIGN) - 未找到父项关键字 和之前没有创建外键关联所不同的是,这里执行了第二句insert SQL后,由于插入roomid的时候插入的是0,而在主表room中没有这样的记录,因此会抛出“未找到父项关键字”的异常。为了解决这个问题,必须在映射文件上做文章。修改UserInfo.hbm.xml文件,使得其在插入userinfo表时不对roomid进行插入,也即保证该字段是个“外源性 ”的字段,见例6.12。 例6.12:修改后的UserInfo.hbm.xml &?xml version='1.0' encoding='UTF-8'?& &!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http:// hibernate. sourceforge.net/hibernate-mapping-3.0.dtd"& &!-- UserInfo实体的package --& &hibernate-mapping package="testhibernate"& &!-- UserInfo实体的class和表 --& &class name="UserInfo" table="userinfo"& &!-- 映射id主键 --& &id name="id" column="id" type="java.lang.Long"& &!-- 映射为自增长类型 --& &generator class="increment" /& &/id& &!-- 映射name属性 --& &property name="name" column="NAME" type="java.lang.String" not-null="true" /& &!-- 映射roomnumber属性 --& &property name="sex" column="SEX" type="java.lang.String"/& &!-- &many-to-one name="room" column="roomid" class="Room" /&--& &!-- 映射roomid属性作为UserInfo实体与Room实体的关联,其字段在userinfo表中 --& &!-- 通过设置insert和update属性为false表示该字段是该外源性的字段,也即该字段的值来源于映射的其他字段 --& &!-- insert和update属性默认状态为true,在当前操作中只需要设置insert="false"即可 --& &property name="roomid" column="roomid" type="java.lang.Long" insert="false" update="false"/& &/class& &/hibernate-mapping& 关于修改的结果见注释,对于这样的改动,Hibernate执行的SQL语句如下: Hibernate: /*插入room表*/ insert into room (NAME, roomnumber, id) values (?, ?, ?) Hibernate: /*插入userinfo表,此时由于设置了insert="false",所以roomid字段不被插入*/ insert into userinfo (NAME, SEX, id) values (?, ?, ?) Hibernate: /*通过一对多的关联映射更新userinfo表中当前记录的roomid字段 实现room表与userinfo表的关联*/ update userinfo set roomid=? where id=? 单向关联满足了一定的业务要求,但是当抓取UserInfo实体而又要同时抓取Room实体的业务时就无法被满足。此时就需要进行双向关联的设置。 6.2.4 Hibernate的双向关联常规实现 Hibernate的双向关联除了在主表(当前可以看作room表)的映射文件中设置一对多(one-to- many)外,还需要在从表(当前可以看作userinfo表)设置多对一(many-to-one)。首先需要在UserInfo.java实体类中增加一个Room实体类型的属性,其代码实现见例6.13。 例6.13:增加Room实体类型属性的UserInfo.java public class UserInfo { //实体人的主键
//人的名字 private S //人的性别 private S //实体房间的主键(非必需的)
//实体房间 private R public Room getRoom() {
} public void setRoom(Room room) { this.room = } //省略其他get/set方法 } 在保证Room实体的映射不变,即Room.hbm.xml不变的情况下,需要修改UserInfo.hbm.xml来实现多对一。其映射配置见例6.14。 例6.14:多对一配置的UserInfo.hbm.xml &?xml version='1.0' encoding='UTF-8'?& &!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate. sourceforge.net/hibernate-mapping-3.0.dtd"& &!-- UserInfo实体的package --& &hibernate-mapping package="testhibernate"& &!-- UserInfo实体的class和表 --& &class name="UserInfo" table="userinfo"& &!-- 映射id主键 --& &id name="id" column="id" type="java.lang.Long"& &!-- 映射为自增长类型 --& &generator class="increment" /& &/id& &!-- 映射name属性 --& &property name="name" column="NAME" type="java.lang.String" not-null = "true" /& &!-- 映射roomnumber属性 --& &property name="sex" column="SEX" type="java.lang.String"/& &!-- 映射room属性作为UserInfo实体与Room实体的关联,其字段在userinfo表中为roomid,其实体为Room --& &many-to-one name="room" column="roomid" class="Room" /& &/class& &/hibernate-mapping& 完成所有这些之后双向关联就配置完毕。 6.2.5 双向关联的实现和问题 接着该是双向关联的实现了,将例6.11的实现代码直接使用,为了呈现结果,可以通过UserInfo实体获取Room实体,再增加一个事务来打印结果,其代码实现见例6.15。 例6.15:双向关联的实现代码 public void run() { //与例6.4相同 … //创建一个新事务来获取Room实体 session = HibernateSessionFactory.currentSession(); tx = session.beginTransaction(); //根据room表的主键抓取Room持久化实体 Room room1 = (Room)session.get(Room.class, room.getId()); //打印各实体信息 System.out.println("Name:" + room1.getName()); System.out.println("Roomnumber:" + room1.getRoomnumber()); System.out.println("Id:" + room1.getId()); //抓取从表UserInfo实体的集合,并迭代打印结果 Iterator it = room1.getUsers().iterator(); while (it.hasNext()) { UserInfo userInfoin = (UserInfo)it.next(); System.out.println("Id:" + userInfoin.getId()); System.out.println("Name:" + userInfoin.getName()); System.out.println("Roomid:" + userInfoin.getRoomid()); System.out.println("Sex:" + userInfoin.getSex()); //通过UserInfo实体的room属性获取Room实体的内容 System.out.println("RoomId:" + userInfoin.getRoom().getId()); System.out.println("Name:" + userInfoin.getRoom().getName()); System.out.println("Roomnumber:"+userInfoin.getRoom(). getRoomnumber()); } tx.commit(); HibernateSessionFactory.closeSession(); } 下面执行例6.11的插入代码,来完成双向关联,其SQL语句如下: Hibernate: /* 插入room表 */ insert into room (NAME, roomnumber, id) values (?, ?, ?) Hibernate: /*插入userinfo表,此时roomid为空*/ insert into userinfo (NAME, SEX, roomid, id) values (?, ?, ?, ?) Hibernate: /*通过一对多的关联映射更新userinfo表中当前记录的roomid字段 实现room表与userinfo表的关联*/ update userinfo set roomid=? where id=? 在执行例6.15的代码后打印结果如下: Id:11117 Name:rw Roomid:0 Sex:M RoomId:25 Name:rwhome Roomnumber:001 可以看到,通过Room实体获取UserInfo实体,再反向获取Room实体完成了。 只是这么实现的话,并没有达到最好的效果。因为SQL执行插入时总是要执行三句SQL,这样在效率上是有问题的。要达到效率上的提高就需要做另一个实现,那就是在配置文件中加入inverse属性。 6.2.6 inverse属性与双向关联 使用双向关联执行三句SQL的原因在于:插入room表后,需要插入根据一对多关联的userinfo表,但是插入userinfo表的前提是session.save(room);,也即通过Room实体来维护二者之间的关系。这也就意味着Room实体需要通过自身包含的UserInfo实体一一更新其外键,达到关联的目的。 而inverse属性就提供了另外一个更好的做法,它将关联关系反向交给UserInfo实体来完成,这也就意味着虽然通过session.save(room);来执行插入,但是却是由UserInfo实体来维护二者之间的关系。所做的更改有两个地方,首先是对Room. hbm.xml中一对多部分的修改,见例6.16。 例6.16:增加inverse属性的一对多 &!-- 通过Room实体的users集合属性映射,级联动作为全部 --& &!-- 将inverse属性设置为true,表示维护动作由UserInfo实体来完成 --& &set name="users" cascade="all" inverse="true"& &!-- 映射User表的外键roomid --& &key column="roomid"&&/key& &!-- 一对多映射class UserInfo --& &one-to-many class="UserInfo"&&/one-to-many& &/set& 其次还需要在实现代码中,将UserInfo与Room实体的关系告诉UserInfo实体,也即让userinfo表的记录得到room表记录的主键。这段实现代码见例6.17。 例6.17:UserInfo实体参考Room实体 public void run() { //创建Room实体 Room room = new Room(); //设置Room.name room.setName("rwhome"); //设置Room.roomnumber room.setRoomnumber("001"); //创建UserInfo实体 UserInfo userInfo = new UserInfo(); //设置UserInfo.name userInfo.setName("rw"); //设置UserInfo.sex userInfo.setSex("M"); //保证UserInfo实体得到与Room实体的关系,以帮助由UserInfo来维护外键关联 userInfo.setRoom(room); //创建UserInfo集合userInfoSet Set userInfoSet = new HashSet(); //添加UserInfo实体到集合userInfoSet userInfoSet.add(userInfo); //设置Room.users(这是一个集合类型) room.setUsers(userInfoSet); //创建Hibernate Session Session session = HibernateSessionFactory.currentSession(); //启动事务 Transaction tx = session.beginTransaction(); //持久化Room实体 session.save(room); //提交事务 tx.commit(); //关闭Hibernate Session HibernateSessionFactory.closeSession(); } 执行插入表操作,其显示出来的SQL语句如下: Hibernate: /* 插入room表 */ insert into room (NAME, roomnumber, id) values (?, ?, ?) Hibernate: /*插入userinfo表,此时roomid通过UserInfo参考Room实体已经获取并插入了*/ insert into userinfo (NAME, SEX, roomid, id) values (?, ?, ?, ?) 这样的SQL语句在批量插入userinfo表时效率高了许多,是双向关联中效率最高的一种插表方式。值得注意的是,执行插表语句中的userInfo.setRoom(room);必须写在代码中,否则SQL语句同样是执行两句插入,但是在userinfo表中将会插入一个为null的roomid。 6.2.7 结语 单向关联的功能比双向关联要弱,而且单向关联在操作数据库表时总是会执行三句SQL。因此在一般设计和实现中,通常应该优先选择使用双向关联。而使用双向关联时,inverse属性也是不能忽视的一个重点。通过多端来控制外键值的插入是值得推荐的。
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!

我要回帖

更多关于 wget 未找到命令 的文章

 

随机推荐