![1302619-20171222162458178-1905840922.jpg](https://i-blog.csdnimg.cn/blog_migrate/00c5d20f2b6b90b4066b4e3d3d546764.jpeg)
![1302619-20171222162458896-317013056.jpg](https://i-blog.csdnimg.cn/blog_migrate/3105d9d191679edcb081dfabd4e6ff92.jpeg)
![1302619-20171222162459662-1822162823.jpg](https://i-blog.csdnimg.cn/blog_migrate/64c14d0219ac4c8ac88e259b32f433f2.jpeg)
![1302619-20171222162459975-2124371238.jpg](https://i-blog.csdnimg.cn/blog_migrate/b8bd0a047dbd21c5c58a59b89c5c14e0.jpeg)
![1302619-20171222162501209-2010628992.jpg](https://i-blog.csdnimg.cn/blog_migrate/bde17c39d4c84b650a490ce57e2ed6c1.jpeg)
![1302619-20171222162502209-2018591142.jpg](https://i-blog.csdnimg.cn/blog_migrate/f1bcb97184dd676e963fd57874c4894f.jpeg)
![1302619-20171222162503053-1657467091.jpg](https://i-blog.csdnimg.cn/blog_migrate/11388f859542df7a39168ae6091c9d83.jpeg)
![1302619-20171222162504021-1020823227.jpg](https://i-blog.csdnimg.cn/blog_migrate/9174430a4462e76020365aab41f4360b.jpeg)
![1302619-20171222162505131-1170497665.jpg](https://i-blog.csdnimg.cn/blog_migrate/686e57986d85725f2cef92d82cba96cf.jpeg)
![1302619-20171222162505662-798100766.jpg](https://i-blog.csdnimg.cn/blog_migrate/aec42f04c5504117594d01a5f5cd23f6.jpeg)
![1302619-20171222162506803-1908108207.jpg](https://i-blog.csdnimg.cn/blog_migrate/8d829d64859f5976db2ff21fed33bb06.jpeg)
![1302619-20171222162507912-2113145738.jpg](https://i-blog.csdnimg.cn/blog_migrate/c88995dde03f5ff95b4c10c2fbd92241.jpeg)
![1302619-20171222162509725-1675114050.jpg](https://i-blog.csdnimg.cn/blog_migrate/c1784768d1c9e6fe98e0c0727210b32d.jpeg)
当要进行测试用例的时候,就从有注解@SpringBootApplication的类BookShopApplication做入口,把整个spring容器组装起来,然后拿需要的bean,然后测试里面的代码
@Transactional就是说所有的测试用例都要在事务下面执行,页就是说在测试用例完了的时候,所有的数据库操作都会回滚,不会执行真正的数据库操作,然后就能保证反复的执行测试用例了
注意:所有的测试用例都应该是:public void (即:没有返回值)
![1302619-20171222162510584-1868929888.jpg](https://i-blog.csdnimg.cn/blog_migrate/09c7d4c92759b815b57ea0be3248b280.jpeg)
![1302619-20171222162511037-2102559316.jpg](https://i-blog.csdnimg.cn/blog_migrate/024ca6736c7e38be012a68b1a1998b47.jpeg)
![1302619-20171222162512693-501190585.jpg](https://i-blog.csdnimg.cn/blog_migrate/70afae3d338b64b08becff5d1bd3ff36.jpeg)
![1302619-20171222162513193-1145545460.jpg](https://i-blog.csdnimg.cn/blog_migrate/a1b797319cd5c03eb358d4dac36f6969.jpeg)
![1302619-20171222162513834-380521793.jpg](https://i-blog.csdnimg.cn/blog_migrate/3f5c7a594d0627d4456131be6143af9a.jpeg)
![1302619-20171222162514412-1482865063.jpg](https://i-blog.csdnimg.cn/blog_migrate/806ef7cce6643e9a20267dbb59bbb521.jpeg)
![1302619-20171222162515475-1208457348.jpg](https://i-blog.csdnimg.cn/blog_migrate/ba4499846b69afc5ce2d641cf6387951.jpeg)
@Entit
y就是把这个bean映射到数据库中去
@id是不敖明这个属性是主键
@GeneratedValue指定当前主键的生成策略
@table(name="roncoo_category")是将实体类映射到数据库中后命名表名为roncoo_category
![1302619-20171222162515865-1113037826.jpg](https://i-blog.csdnimg.cn/blog_migrate/f57489b0872ca6c9af27b89ac89ab946.jpeg)
意思是将name映射到数据库中的字段名命名为roncoo
![1302619-20171222162516256-1979737142.jpg](https://i-blog.csdnimg.cn/blog_migrate/7b81382e1d744e8e9322e78698680a69.jpeg)
新建一个命名策略
用来生成表名和字段名的前缀
![1302619-20171222162517693-667403572.jpg](https://i-blog.csdnimg.cn/blog_migrate/4d462f76774fd57b437c6db788dffe06.jpeg)
![1302619-20171222162519334-1269513668.jpg](https://i-blog.csdnimg.cn/blog_migrate/f5019a8054580c6999dede53cb28823e.jpeg)
![1302619-20171222162520631-1156727875.jpg](https://i-blog.csdnimg.cn/blog_migrate/3cfcf4d569fbc1c2890d7c078b5378dd.jpeg)
![1302619-20171222162521100-636711161.jpg](https://i-blog.csdnimg.cn/blog_migrate/518ee1fbf0502f8819ba5d23b10f20f5.jpeg)
发现bean中的string类型属性的会自动在数据库中转化成varchar类型的字段
long类型会自动转成bigint
date类型会自动转成datetime
![1302619-20171222162521350-997463650.jpg](https://i-blog.csdnimg.cn/blog_migrate/a81e6432cd2cebc17a0f483eaf043f60.jpeg)
![1302619-20171222162521646-345293030.jpg](https://i-blog.csdnimg.cn/blog_migrate/a8cb1a04327705b04e089046d463216b.jpeg)
![1302619-20171222162522850-1905175404.jpg](https://i-blog.csdnimg.cn/blog_migrate/2adea9e5d859144ece336a2e7e9f2063.jpeg)
内嵌对象的映射
@Embeddable
意思是说这是一个可嵌入的对象(可注入)
![1302619-20171222162524365-1385044372.jpg](https://i-blog.csdnimg.cn/blog_migrate/1ca7e425b272c3b176d7d03ce64f44cb.jpeg)
(可被注入)
![1302619-20171222162525365-514538129.jpg](https://i-blog.csdnimg.cn/blog_migrate/dadb5808ffdc9f4910be7bda073723af.jpeg)
![1302619-20171222162526240-322688699.jpg](https://i-blog.csdnimg.cn/blog_migrate/22206eb39d4095e5972ae9b077b4ea22.jpeg)
这个就叫做内嵌对象的映射,我们可以在任何一个需要地址信息的类中嵌入address,
集合映射
如何映射集合?
如下例子
![1302619-20171222162527459-23332023.jpg](https://i-blog.csdnimg.cn/blog_migrate/4c3035dc2a7657e0b7b22a92db2b69e2.jpeg)
,执行后,发现多出来一张表
![1302619-20171222162527756-152511383.jpg](https://i-blog.csdnimg.cn/blog_migrate/3c4be6738a76b799ae003cec7c141f72.jpeg)
![1302619-20171222162529771-1947394194.jpg](https://i-blog.csdnimg.cn/blog_migrate/e330c20f66d2483d77f0df6c95033d59.jpeg)
![1302619-20171222162531193-322339203.jpg](https://i-blog.csdnimg.cn/blog_migrate/24086e67bc4c66a8b55308572bccc4e9.jpeg)
![1302619-20171222162532131-2119782180.jpg](https://i-blog.csdnimg.cn/blog_migrate/95ff2b12b157fac78050aa151c15cf91.jpeg)
单向多对一映射
图书和category是多对一的关系,多本书可以属于一个门类,所以用@manyToOne
这本书是属于哪个门类的
![1302619-20171222162533115-1027018014.jpg](https://i-blog.csdnimg.cn/blog_migrate/621c4594d48f17b5b376b111cbc23bfd.jpeg)
![1302619-20171222162534115-1084256485.jpg](https://i-blog.csdnimg.cn/blog_migrate/a2039e5526483a2599d25be63fb7c21e.jpeg)
category_id是一个外键,指向
category表的主键,就产生了一个主外键关系
因为是单向的,所以只能通过book访问到category,而不能通过category门类访问到门类下面有哪些book的,这个叫做单向多对一的关系
单向一对多的关系
这个是要建立在Category里面的,Category和book是一对多的关系,一个门类里面可以有多个book
![1302619-20171222162535209-1054254321.jpg](https://i-blog.csdnimg.cn/blog_migrate/8d61fed7bc4d4f7b9f098e42bbe33298.jpeg)
![1302619-20171222162536037-1985662408.jpg](https://i-blog.csdnimg.cn/blog_migrate/e6932cecd22eb3853fae043a928f38d9.jpeg)
![1302619-20171222162536928-1187935881.jpg](https://i-blog.csdnimg.cn/blog_migrate/727bef602ddd67b808c8fe374f92e03c.jpeg)
这个因为是单向一对多,通过门类可以访问门类下的book,但是从图书book是访问不到Category门类的
维护方是在一的一方,而不是在多的那边,当一对多是在一的一方维护的时候,他是用一张新的表来维护这个关系的;
而在维护一对多,是在多的一方维护的话,就会在多的一方产生一个外键来指向一个一方的主键
所以标准的应该是:
在维护一对多,在多的一方用产生外键来维护,而不是在一的一方用产生一张新表的方式来维护,
一般情况下是不太建议使用单向一对多关系,但是如果非要建立单向一对多关系
,应该在多的一方使用@ManyToOne,而不是在一的一方使用@OneToMany
但是最佳实践是建立一个双向关系
![1302619-20171222162537506-1660477227.jpg](https://i-blog.csdnimg.cn/blog_migrate/f53b5ad0b8aac54426a5d643c8064fde.jpeg)
![1302619-20171222162538225-1440442873.jpg](https://i-blog.csdnimg.cn/blog_migrate/b13acb926d2c4e2e21c6ef526dd36e66.jpeg)
在多的一方使用@ManyToOne,并且在一的一方使用@OneToMany,
当拿到book对象的时候,能通过book中的category的getCategory()拿取到一的一方
category的门类的信息
当拿到门类信息category的时候,通过getBooks()拿到门类下的所有图书book的信息
当有一对多或多对一的关系的时候比如:在数据库中查出一个book对象来的时候,他会用一个select语句从book表中查出一条记录来把他转成一个book对象,当在调book对象的getCategory()方法的时候,他会立刻执行一条sql,把相应的门类信息查出来,放到这
同样的,当查Category,掉Category对象的getBook()方法,也会执行一条SQL,把门类下的所有book读出来,放到
的
中
![1302619-20171222162538475-286180846.jpg](https://i-blog.csdnimg.cn/blog_migrate/6f41fd8e84a8d8f0fa0e448ab3a64f64.jpeg)
![1302619-20171222162538709-432458460.jpg](https://i-blog.csdnimg.cn/blog_migrate/844810dcfcdc71f41289c5f78b9fd850.jpeg)
因为加了@ManyToOne和@OneToMany,所以两边都在维护这个关系,在
@OneToMany这边维护的时候,还是建了多余的这张表,现在的需求就是希望在多的一方book对象中通过产生外键来维护,一的一方仍然声明@OneToMany,通过Category可以访问下面的books,希望是一个双向关联
![1302619-20171222162539209-912966537.jpg](https://i-blog.csdnimg.cn/blog_migrate/c26f3c65bed3cce227f37aa69d1515cf.jpeg)
一对多的关系不由一的这一端来维护,而交给多的那一端,也就是book里面的Category属性来维护,即
@ManyToOne生成外键的属性,做了这个设置之后Category就放弃了一对多的这样一个管理,不会再生成那张多余的表,只是有多的这端,也就是Book这端生成一个外键来维护这个关系
![1302619-20171222162539771-272174945.jpg](https://i-blog.csdnimg.cn/blog_migrate/4f294e53983a3e41344c34eec5d64ce1.jpeg)
![1302619-20171222162540834-1860750804.jpg](https://i-blog.csdnimg.cn/blog_migrate/af8df89b7f9e06c9b981747738cd7add.jpeg)
@OneToMany(@mappedBy="category")的意思是放弃去管理一对多的关系(即:不多生成一张表,只提供能从一的一方查到多的一方这种能力),只在多的一端去管理
下面介绍几个@ManyToOne常用的属性:
![1302619-20171222162541756-884458111.jpg](https://i-blog.csdnimg.cn/blog_migrate/fa392042c74109387e6a88c8bc5d6ec2.jpeg)
![1302619-20171222162542506-732344991.jpg](https://i-blog.csdnimg.cn/blog_migrate/8605dbc158b5fe5183a4f4ff2ae6294b.jpeg)
![1302619-20171222162543256-2078501894.jpg](https://i-blog.csdnimg.cn/blog_migrate/661da948b4acef8908a7199dfb6e173f.jpeg)
fetch是实体的加载方式,有两个值,一个是eager,还有一个是lazy,
lazy就是在读取book
信息的时候,不执行一个关联查询,不去查门类Category信息;
eager是读取book信息的时候(读取book的SQL是一个关联查询,会关联到Category表上去)
,同时把门类的信息查出来
不关联到另一个表查询的是lazy,关联到另一个表查的是eager
当用
eager这种策略的时候,当查出一个book的时候,Category对象里面的属性都已经被填充好了,因为他已经将数据从category表中查出来了
而当使用lazy这个策略查一个book的时候,Category中是没有相应的信息的,当用getCategory()去get某一个属性的时候他才又执行一个select,把Category的信息查出来
注意在
@ManyToOne中默认的抓取策略是eager,不写的时候,查book默认的是把Category的信息也带出来
可以控制相关策略的抓取控制
![1302619-20171222162544084-165809079.jpg](https://i-blog.csdnimg.cn/blog_migrate/ff67b27476cce4d855741071a626f7c8.jpeg)
optional是用来标识category这个外键字段是否可以为空,默认是true,可以为空,即书可以不属于任何一个门类
如果改成false就是说book必须有一个门类,不能存在某有门类的book
![1302619-20171222162545100-912778077.jpg](https://i-blog.csdnimg.cn/blog_migrate/63d044870273df854848070a2b604ba5.jpeg)
cascade这是表示与实体关联的那些实体的级联处理的类型,默认情况下在@ManyToOne这一端是不会设这个类型的
下面介绍几个
@OneToMany
常用的属性
其中也有fetch=fetchType.lazy和
fetch=fetchType.eager属性外,还多出了orphanRemoval属性
orphanRemoval:
![1302619-20171222162546365-1237599416.jpg](https://i-blog.csdnimg.cn/blog_migrate/984ca28b5f0840e0160519a40f31e476.jpeg)
orphanRemoval用来表示当前指定集合中的一个元素被从集合中移除后是否从数据库中删除,即:假如book中有5本书,然后用list的remove方法(例如:books.remove(1)
),把一本书挪出去了,只是把他从集合中去掉了,从集合中去掉以后这个Book就不属于任何一个门类了,那么
是不是把这本书删掉,就是由orphanRemoval控制的,当等于orphanRemoval=true时,当把那本书从集合中移除掉,这本书会自动被删掉,默认的是false
![1302619-20171222162547506-2027016072.jpg](https://i-blog.csdnimg.cn/blog_migrate/aa334790b4c7906a4ab749ab69e6a07f.jpeg)
cascade=CascadeType.REMOVE这是一个级联的操作,当删除一个Category的时候,把这个门类下所对应的所有图书,一块删掉。
默认情况下,这个属性是空的什么都不做的,也就是说把门类删掉,不会影响里面的书,删的时候会报错的,因为有外键了
![1302619-20171222162547818-1950234622.jpg](https://i-blog.csdnimg.cn/blog_migrate/dfde70d3ae9b96e04acd0a71ca53320d.jpeg)
多对多关系的映射
在这个案例里面,图书和作者是多对多关系,一个作者可以写多本书,一本书可以有多个作者
创建了一个中间对象BookAuthor,用来保存多对多关系,在这个
对象中
有一个指向book的引用,
还有一个指向作者author的引用
![1302619-20171222162548709-1237244970.jpg](https://i-blog.csdnimg.cn/blog_migrate/9c5d2d49037148e73e166edf942505b6.jpeg)
想要知道这个作者写了哪些书,不是直接指向book的(直接指向book就变成了,一对多关系了),
而要指向的这个中间对象
BookAuthor,由BookAuthor对象中的author属性来管理
一对多的关系
![1302619-20171222162549881-1132898148.jpg](https://i-blog.csdnimg.cn/blog_migrate/b869cf73c70a8c770927fafc47c667b7.jpeg)
同样在book中也要写上面的这个属性
意思是说这个book是由哪些作者写成的,同样也指向BookAuthor这个中间对象,告诉book对象,是靠
BookAuthor中的
book属性来维护这个一对多关系的
![1302619-20171222162550803-149557163.jpg](https://i-blog.csdnimg.cn/blog_migrate/60dff846dff2976e6ceec6657dfe06d4.jpeg)
在bookauthor对象中存了多对多的关系
![1302619-20171222162551771-1549048193.jpg](https://i-blog.csdnimg.cn/blog_migrate/fc036691813916f9220289ec216bd65f.jpeg)
在多对多关系中,为什么不用@ManyToMany呢,因为spring官方建议将多对多关系最好拆成两个一对多关系
注意:
![1302619-20171222162552084-1735344393.jpg](https://i-blog.csdnimg.cn/blog_migrate/799dc3847b7a7d98d4611883e5e76d6e.jpeg)
![1302619-20171222162552412-1963892233.jpg](https://i-blog.csdnimg.cn/blog_migrate/094331dadedfa4bbca5cc1a84d833b1c.jpeg)
这个注解的作用是,当通过作者的一个getbooks方法拿取作者写额所有的书的时候,这些书就会按照书名升序排列
一对一关系的映射
比如说用户表可能有30个字段,但是这些字段中只有10个字段是比较常用的,有些就不常用,如果把这所有的字段放到一个表中去,然后每次查表都会查许多字段,所以一般就会将一张表拆成两张表,一个表放常用的,另一个表放不常用的
![1302619-20171222162553412-1449997553.jpg](https://i-blog.csdnimg.cn/blog_migrate/8201cb2a2b92ceec3d016a12ca116b6d.jpeg)
![1302619-20171222162554068-733331387.jpg](https://i-blog.csdnimg.cn/blog_migrate/8940effa981e4654dbe60bfc9308b080.jpeg)
![1302619-20171222162554850-1561375211.jpg](https://i-blog.csdnimg.cn/blog_migrate/7c2bc0c94605b555fe4c9872e7c29db2.jpeg)
authorInfo不去管理一对一的关系,而将管理权交到Author这个对象的Info属性上(即交到会去建外键的属性上),但是可以由
authorInfo表查到author表这一方
![1302619-20171222162555725-1514455063.jpg](https://i-blog.csdnimg.cn/blog_migrate/526a05d874c0080a2d37baced193270f.jpeg)
注意:在一对多的关系中,一的一方维护权是用建表的方式完成的(是不建议的),
多的一方的维护权是通过生成外键的方式完成的(建议)。