简体   繁体   中英

Hibernate not saving complex objects

I have the following object that I would like to save using hibernate:

@Entity
@Table(name = "setting")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Setting
{
    @Id
    @Column(name = "id")
    public String internalID;
    @Column(name = "name")
    public String name;
    @Column(name = "type")
    public String type;
    @Column(name = "regex")
    public String regex;

    @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(name = "setting",
           joinColumns = {@JoinColumn(name = "parent_id")},
           inverseJoinColumns = {@JoinColumn(name = "id")})
    public Collection<Setting> children;

    @JsonIgnore
    @Column(name = "parent_id")
    public String parentID;

    @Column(name = "value")
    public String value;

    public Setting()
    {
        this.internalID = UUID.randomUUID().toString();
    }
}

Hibernate tries to save this, but it seems to fail with the child objects. When creating a Setting object that is 3 layers deep, only the first 2 layers are saved and the third one is ignored.

Beside that the second layer is only saving the internalID and parentID, leaving name, type and regex null in the database.

save function for the database:

public static void saveObject(Object obj)
{
    if(sessionFactory == null)
        init();
    try(Session session = sessionFactory.openSession())
    {
        session.beginTransaction();
        session.save(obj);
        session.getTransaction().commit();
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
}

What is it that I am missing? Are the annotations wrong?

Steps to reproduce:

public static void test()
{
    Setting outer = new Setting();
    outer.name = "Outer";
    Setting inner = new Setting();
    inner.name = "Inner";
    outer.children = new ArrayList<>();
    outer.children.add(inner);
    saveObject(outer);
}

public static void saveObject(Object obj)
{
    if(sessionFactory == null)
        initDB();
    try(Session session = sessionFactory.openSession())
    {
        session.beginTransaction();
        session.save(obj);
        session.getTransaction().commit();
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
}

private static SessionFactory sessionFactory;

private static void initDB()
{
    final StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure() // configures settings from hibernate.cfg.xml
            .build();
    sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();
    Runtime.getRuntime().addShutdownHook(new Thread(() ->
    {
        StandardServiceRegistryBuilder.destroy(registry);
    }));
}

with the SQL:

CREATE SCHEMA `cosmos` ;

CREATE TABLE `cosmos`.`setting` (
  `id` VARCHAR(64) NOT NULL,
  `name` VARCHAR(256) NULL,
  `type` VARCHAR(7) NOT NULL,
  `regex` VARCHAR(512) NULL,
  `value` VARCHAR(512) NULL,
  `parent_id` VARCHAR(64) NULL,
  PRIMARY KEY (`id`));

CREATE TABLE `cosmos`.`setting_relation` (
  `id` VARCHAR(64) NOT NULL,
  `parent_id` VARCHAR(64) NULL,
  PRIMARY KEY (`id`));

and the config:

<hibernate-configuration>
    <session-factory name="localConnection">
        <property name="hibernate.dialect">
            org.hibernate.dialect.MySQLDialect
        </property>
        <property name="hibernate.connection.driver_class">
            com.mysql.jdbc.Driver
        </property>

        <property
                name="hibernate.connection.url">
            jdbc:mysql://localhost:3306/cosmos?useSSL=false
        </property>
        <property name="hibernate.connection.username">
            root
        </property>
        <property name="hibernate.connection.password">
            ****
        </property>

        <!-- List of XML mapping files -->
        <mapping class="se.mulander.cosmos.settings.model.Setting"/>
    </session-factory>
</hibernate-configuration>

try @JoinTable(name = "setting_some_name" instead of @JoinTable(name = "setting"

@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(name = "setting_some_name",
        joinColumns = {@JoinColumn(name = "parent_id")},
        inverseJoinColumns = {@JoinColumn(name = "id")})
public Collection<Setting> children;

or if you want to do it with self join on same table , do this :

@ManyToOne(optional = true)
@JoinColumn(name = "parent_id")
public Setting parent;

@OneToMany(fetch = FetchType.EAGER, mappedBy = "parent" , cascade = {CascadeType.ALL})
public Collection<Setting> children;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM