Reputation: 2999
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>
Upvotes: 0
Views: 683
Reputation: 5407
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;
Upvotes: 1