zoukankan      html  css  js  c++  java
  • hibernate5(12)注解映射[4]一对一外键关联

    在实际博客站点中,文章内容的数据量非常多,它会影响我们检索文章其他数据的时间,如查询公布时间、标题、类别的等。

    这个时候,我们能够尝试将文章内容存在还有一张表中,然后建立起文章——文章内容的一对一映射

    一对一关联有两种方式,一种是外键关联。还有一种是复合主键关联。

    外键关联

    以下我们先看一个一对一单向关联的实例

    /*************关联关系维护方************/
    @Table(name = "t_article")
    @Entity
    public class Article {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Integer id;
        private String title;
        @OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY,orphanRemoval = true,targetEntity = ArticleContent.class)
        @JoinColumn(name = "article_content_id")
        private ArticleContent articleContent;
        //忽略get和set方法
    }

    以下是我们的文章内容类

    @Table(name = "t_article_content")
    @Entity
    public class ArticleContent {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Integer id;
        @Lob
        private String content;
    
        //忽略get和set方法
    }

    以下是我们的測试类

    public class Test3 {
        private ApplicationContext ac;
        private SessionFactory sessionFactory;
        private Session session;
        private Transaction transaction;
        @BeforeClass//在測试类初始化时调用此方法,完毕静态对象的初始化
        public static void before(){
        }
        @Before//每个被注解Test方法在调用前都会调用此方法一次
        public void setup(){//建立针对我们当前測试方法的的会话和事务
            ac = new ClassPathXmlApplicationContext("spring-datasource.xml");
            sessionFactory = (SessionFactory) ac.getBean("sessionFactory");
            session = sessionFactory.openSession();
            transaction = session.beginTransaction();
        }
        //測试级联关系映射注解配置:一对一单向关联
        @Test
        public void test1(){
            //測试级联加入
            Article article = new Article();
            article.setTitle("title");
            ArticleContent articleContent = new ArticleContent();
            articleContent.setContent("content");
            article.setArticleContent(articleContent);//建立映射关系
            session.save(articleContent);
            session.save(article);
            //測试级联删除
    //      Article article = (Article) session.get(Article.class,1);
    //      session.delete(article);
        @After//每个被注解Test方法在调用后都会调用此方法一次
        public void teardown(){
            if(transaction.isActive()){//假设当前事务尚未提交,则
                transaction.commit();//提交事务,主要为了防止在測试中已提交事务,这里又反复提交
            }
            session.close();
    }

    调用我们的測试方法test1。

    控制台打印:
    Hibernate: insert into t_article_content (content) values (?)
    Hibernate: insert into t_article (article_content_id, title) values (?, ?)
    此时查看数据库:

    mysql> show tables; ————————————hibernate帮我们新建的表格
    +———————+
    | Tables_in_hibernate |
    +———————+
    | t_article |
    | t_article_content |
    +———————+
    2 rows in set (0.00 sec)

    mysql> desc t_article; ————————————单方维护映射关系,通过article_content_id维护
    +——————–+————–+——+—–+———+—————-+
    | Field | Type | Null | Key | Default | Extra |
    +——————–+————–+——+—–+———+—————-+
    | id | int(11) | NO | PRI | NULL | auto_increment |
    | title | varchar(255) | YES | | NULL | |
    | article_content_id | int(11) | YES | MUL | NULL | |
    +——————–+————–+——+—–+———+—————-+
    3 rows in set (0.00 sec)

    mysql> desc t_article_content;
    +———+———-+——+—–+———+—————-+
    | Field | Type | Null | Key | Default | Extra |
    +———+———-+——+—–+———+—————-+
    | id | int(11) | NO | PRI | NULL | auto_increment |
    | content | longtext | YES | | NULL | |
    +———+———-+——+—–+———+—————-+
    2 rows in set (0.00 sec)

    mysql> select * from t_article;
    +—-+——-+——————–+
    | id | title | article_content_id |
    +—-+——-+——————–+
    | 1 | title | 1 |
    +—-+——-+——————–+
    1 row in set (0.00 sec)

    mysql> select * from t_article_content;
    +—-+———+
    | id | content |
    +—-+———+
    | 1 | content |
    +—-+———+
    1 row in set (0.00 sec)

    凝视掉測试代码的级联加入部分,执行级联删除部分:

    Hibernate: delete from t_article where id=?
    Hibernate: delete from t_article_content where id=?


    在这里,我们观察到它是先删除文章(维护关系方)。再删除t_article_content的,回忆我们之前的一对多关联測试。都是先删除维护关系方的。这事实上非常好理解,我们肯定要清除掉相应的关联关系(体如今数据库的外键上)才干完毕被关联内容的删除操作

    一对一双向关联非常easy,直接在articleContent上加入:

    @OneToOne(cascade = CascadeType.ALL,mapperBy = "articleContent")
    private Article article;
    //忽略getter/setter

    使用和上面一样的測试代码。hibernate会帮我们生成表格并插入数据:

    mysql> select * from t_article_content;
    +—-+———+
    | id | content |
    +—-+———+
    | 1 | content |
    +—-+———+
    1 row in set (0.00 sec)

    mysql> select * from t_article;
    +—-+——-+——————–+
    | id | title | article_content_id |
    +—-+——-+——————–+
    | 1 | title | 1 |
    +—-+——-+——————–+
    1 row in set (0.00 sec)

    这时候假设我们尝试在放弃维护的articleContent端进行级联加入:

    //測试articleContent级联加入
    Article article = new Article();
    article.setTitle("title");
    ArticleContent articleContent = new ArticleContent();
    articleContent.setContent("content");
    articleContent.setArticle(article);
    session.save(articleContent);

    我们的article对象能被成功保存。可是。两者的关联关系建立失败:

    mysql> select * from t_article_content;
    +—-+———+
    | id | content |
    +—-+———+
    | 1 | content |
    | 2 | content |
    +—-+———+
    2 rows in set (0.00 sec)

    mysql> select * from t_article;
    +—-+——-+——————–+
    | id | title | article_content_id |
    +—-+——-+——————–+
    | 1 | title | 1 |
    | 2 | title | NULL |
    +—-+——-+——————–+
    2 rows in set (0.00 sec)

    这时候我们再尝试从放弃维护端删除:

    //这次删除是有级联关系的
    ArticleContent articleContent = (ArticleContent) session.get(ArticleContent.class, 1);//注意这里id为1
    session.delete(articleContent);

    mysql> select * from t_article_content;
    +—-+———+
    | id | content |
    +—-+———+
    | 5 | content |
    +—-+———+
    1 row in set (0.00 sec)

    mysql> select * from t_article;
    +—-+——-+——————–+
    | id | title | article_content_id |
    +—-+——-+——————–+
    | 6 | title | NULL |
    +—-+——-+——————–+
    1 row in set (0.00 sec)

    会看到我们相应article对象也被删除了!因此,我们须要明白放弃维护关联关系并不代表放弃关联关系,从ArticleContent端,我们一样能进行与关联关系双管的级联加入、删除操作。仅仅是不正确两者关系进行维护。因而在加入时Article端的外键属性article_content_id=null
    我们使用mappedBy属性放弃关联。但级联操作依旧有效,因此须要区分开维护关联关系级联操作的差别。

    这里须要特别注意的是。在这样的一对一映射中。我们最好选择一个被动方并设定mapperBy属性。即让一方放弃维护关联关系,否则,我们会看到下述现象:
    mysql> desc t_article;
    +——————–+————–+——+—–+———+—————-+
    | Field | Type | Null | Key | Default | Extra |
    +——————–+————–+——+—–+———+—————-+
    | id | int(11) | NO | PRI | NULL | auto_increment |
    | title | varchar(255) | YES | | NULL | |
    | article_content_id | int(11) | YES | MUL | NULL | |
    +——————–+————–+——+—–+———+—————-+
    3 rows in set (0.00 sec)

    mysql> desc t_article_content;
    +————+———-+——+—–+———+—————-+
    | Field | Type | Null | Key | Default | Extra |
    +————+———-+——+—–+———+—————-+
    | id | int(11) | NO | PRI | NULL | auto_increment |
    | content | longtext | YES | | NULL | |
    | article_id | int(11) | YES | MUL | NULL | |
    +————+———-+——+—–+———+—————-+
    3 rows in set (0.00 sec)

    两个表中都建立了关于对方的关联映射。

    这是全然没有必要的,并且这样会造成的更严重后果,我们来測试级联加入
    先调用例如以下測试代码:

    //測试article级联加入
    Article article = new Article();
    article.setTitle("title");
    ArticleContent articleContent = new ArticleContent();
    articleContent.setContent("content");
    article.setArticleContent(articleContent);
    session.save(article);

    再调用例如以下測试代码:

    //測试articleContent级联加入
    Article article = new Article();
    article.setTitle("title");
    ArticleContent articleContent = new ArticleContent();
    articleContent.setContent("content");
    articleContent.setArticle(article);
    session.save(articleContent);

    我们会看到数据库相应记录:

    mysql> select * from t_article;
    +—-+——-+——————–+
    | id | title | article_content_id |
    +—-+——-+——————–+
    | 1 | title | 1 |
    | 2 | title | NULL |
    +—-+——-+——————–+
    2 rows in set (0.00 sec)

    mysql> select * from t_article_content;
    +—-+———+————+
    | id | content | article_id |
    +—-+———+————+
    | 1 | content | NULL |
    | 2 | content | 2 |
    +—-+———+————+
    2 rows in set (0.00 sec)

    即两方各维护各的关联关系。假设这时候我们尝试交换測试级联删除:

    Article article = (Article) session.get(Article.class,2);
    session.delete(article);

    会看到例如以下结果:

    mysql> select * from t_article;
    +—-+——-+——————–+
    | id | title | article_content_id |
    +—-+——-+——————–+
    | 1 | title | 1 |
    +—-+——-+——————–+
    1 row in set (0.00 sec)

    mysql> select * from t_article_content;
    +—-+———+————+
    | id | content | article_id |
    +—-+———+————+
    | 1 | content | NULL |
    | 2 | content | 2 |
    +—-+———+————+
    2 rows in set (0.00 sec)

    即级联删除失败了,而这是显然的。由于id为2的文章,相应article_content_id属性为null,在文章方看来,两者都没建立关联关系。这样的时候肯定不是报错就是级联删除失败,而报错是由于假设设置了数据库在t_article_content中设置了对article_id的的外键关联,由于存在记录article_id=2,这时候我们尝试删除article表中id为2的记录,则会由于外键关系约束失败而报错

  • 相关阅读:
    Brain network involved in autonomic functions 与自主功能相关的大脑网络
    Brief summary of classical components of ERP 事件相关成分(ERP)经典成分小结
    ICA & Percentage Variance Account For (PVAF)
    数据处理中白化Whitening的作用图解分析
    Loadings vs eigenvectors in PCA 主成分分析(PCA)中的负荷和特征向量
    主成分分析(PCA)和独立成分分析(ICA)相关资料
    Sketch of heart and QRS complex 心脏及QRS波群简图
    Brain Network visulation in EEG 脑电网络可视化
    Phase Locking Value (PLV) 神经信号的锁相值
    ubuntu16.04下的一些基本操作笔记
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/7354161.html
Copyright © 2011-2022 走看看