I have 2 entities - User and Role with mapping #ManyToOne. I want to change role in user, but Role want's to be updated too.
User entity:
#ManyToOne
#JoinColumn(name = "role_id", insertable = false, updatable = false)
private Role role;
role entity:
#OneToMany(mappedBy = "role")
private Set<User> users;
The error I get is:
java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.spring.model.Role
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:144)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1434)
My tables in DB are not set with CASCADE UPDATE or INSERT. I wasn't able to find appropriate solution. Thanks for your help
EDIT:
This is how I update User
public void update(User user) {
User entity = dao.findById(user.getId());
if(entity!=null) {
entity.setRole(user.getRole());
}
}
EDIT2:
My hibernate configuration
#Configuration
#EnableTransactionManagement
#ComponentScan({ "com.spring.configuration" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {
#Autowired
private Environment environment;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.spring.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
You should use the cascade attribute of the OneToMany annotation, it does not have any relationship with the database cascade operations (but can be affected by that).
#OneToMany(cascade = CascadeType.ALL, mappedBy="role", orphanRemoval = true)
private Set<User> users;
This way the operation will be propagated to the collection elements. You can check this answer for more info.
You can also modify the cascade property of the ManyToOne annotation.
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "role_id")
private Role role;
Related
I am using Springboot for an app that uses some classes that are managed in another application and stored in another database. My Flight class, which is managed in the current application in a local database, has an attribute of type Aircraft, which is also defined in the current application but is managed in the other application and stored in another database.
The Flight class:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(schema = "app1")
public class Flight implements Serializable {
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "flight_sequence"
)
#SequenceGenerator(
name = "flight_sequence",
allocationSize = 1
)
#Column(nullable = false, updatable = false)
private Long id;
private String callsign;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="aircraft_id", nullable=false)
private Aircraft aircraft;
private Date date;
private Operator operator;
private String origin;
private String destination;
}
My Aircraft class:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(schema = "app2")
public class Aircraft implements Serializable {
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "aircraft_sequence"
)
#SequenceGenerator(
name = "aircraft_sequence",
allocationSize = 1
)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="operator_id", nullable=false)
private Operator operator;
private String registration;
private String acType;
}
I wrote a Jpa query in a FlightRepository which uses as a parameter the registration attribute from the Aircraft attribute of the Flight class:
public interface FlightRepository extends JpaRepository<Flight, Long> {
Flight findFirstByDestinationAndAircraftRegistrationOrderByDateDesc(String destination, String registration);
}
but this results in the following exception:
Caused by: org.hibernate.AnnotationException: #OneToOne or #ManyToOne on com.student.application.domain.app1.Flight.aircraft references an unknown entity: com.student.application.domain.app2.Aircraft
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:100)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processEndOfQueue(InFlightMetadataCollectorImpl.java:1750)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1694)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1623)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:295)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1460)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1494)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
... 86 more
If I add a #OneToMany annotation to the Aircraft class the problem remains the same. If I remove both the #OneToMany and #ManyToOne annotations, I get a Cannot join to attribute of basic type exception.
These are the configuration classes for the two databases:
#Configuration
#PropertySource({"classpath:application.properties"})
#EnableJpaRepositories(
basePackages = "com.student.application.repository.app1",
entityManagerFactoryRef = "app1EntityManager",
transactionManagerRef = "app1TransactionManager")
public class App1DBConfiguration {
#Autowired
private Environment env;
#Primary
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource app1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean app1EntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(app1DataSource());
em.setPackagesToScan(
"com.student.application.domain.app1");
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("spring.jpa.hibernate.ddl-auto"));
properties.put("hibernate.dialect",
env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("hibernate.dialect.storage_engine",
env.getProperty("spring.jpa.properties.hibernate.dialect.storage_engine"));
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean
public PlatformTransactionManager app1TransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
app1EntityManager().getObject());
return transactionManager;
}
}
#Configuration
#PropertySource({"classpath:application.properties"})
#EnableJpaRepositories(
basePackages = "com.student.application.repository.app2",
entityManagerFactoryRef = "app2EntityManager",
transactionManagerRef = "app2TransactionManager")
public class App2DBConfiguration {
#Autowired
private Environment env;
#Bean
#ConfigurationProperties(prefix = "spring.app2")
public DataSource app2DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean app2EntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(app2DataSource());
em.setPackagesToScan(
"com.student.application.domain.app2");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
"none");
properties.put("hibernate.implicit_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public PlatformTransactionManager app2TransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
app2EntityManager().getObject());
return transactionManager;
}
}
One flight can have just one Aircraft but one aircraft can have more than one flight... so on aircraft you should have
#OneToMany(mappedBy = "Flight") Join column is not needed here
Im assuming you want it to be mapped by flight and have all the info stored in flight table. you could allso join tables and make flight_aircraft table.
annotations On Flight ->
#ManyToOne
#JoinColumn(name="aircraft_id")
I have an Spring boot application with one entity. I generated an mysql schema and now I am trying to generate a table inside this when my spring boot application is starting I tried this with doing custom configuration classes but it doesnt generate any tables.
Configuration Class:
#Configuration
#PropertySource({"classpath:persistence-multiple-db.properties"})
#EnableJpaRepositories(
basePackages = "com.cbc.coorporateblinddateservice.repositories.meetinsSetting",
entityManagerFactoryRef = "meetingSettingEntityManager",
transactionManagerRef = "meetingSettingTransactionManager"
)
public class MeetingSettingsConfig {
#Autowired
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean meetingSettingEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(meetingSettingDataSource());
em.setPackagesToScan(
"com.cbc.coorporateblinddateservice.entities.meetinsSetting");
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean
public DataSource meetingSettingDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("meetingSetting.jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("meetingSetting.jdbc.url"));
dataSource.setUsername(env.getProperty("meetingSetting.jdbc.user"));
dataSource.setPassword(env.getProperty("meetingSetting.jdbc.pass"));
dataSource.setSchema("coorporate_blinddate");
return dataSource;
}
#Primary
#Bean
public PlatformTransactionManager meetingSettingTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
meetingSettingEntityManager().getObject());
return transactionManager;
}
}
Entity:
#Entity
#Table(name = "MeetingsSetting")
public class MeetingsSetting {
#Id
#GeneratedValue
private Long id;
#Column(name = "meeting_name")
private String meetingName;
#Column(name = "meeting_url")
private String meetingUrl;
#Column(name = "meeting_pw")
private String meetingPw;
//
// #OneToMany(mappedBy = "meetingsSetting", cascade = CascadeType.ALL)
// private List<Date> meetingDate = new ArrayList<>();
}
And finally my config.property:
meetingSetting.jdbc.driverClassName=com.mysql.jdbc.Driver
meetingSetting.jdbc.url=jdbc:mysql://localhost:3306/Coorporate_Blinddate?createDatabaseIfNotExist=true&useSSL=true&serverTimezone=UTC
meetingSetting.jdbc.user=root
meetingSetting.jdbc.pass=
I know it takes my config.property because if I enter the wrong user it gives me an error. But know I want to generate the table directly from my entity class.
spring.jpa.hibernate.ddl-auto=create in application.properties file
this requires Spring JPA in your project
The same question has been asked number of times but none of them has solution to my problem.
I have created a hibernate + H2 + Sprinvg mvc project. I am using java based configuration. I have the following beans related to Datasource, SessionFactory and TransactionManager
#Configuration
#ComponentScan(basePackages="org.testpackage")
#EnableWebMvc
#EnableTransactionManagement
public class MyConfiguration extends WebMvcConfigurationSupport {
#Bean(initMethod="start",destroyMethod="stop")
public org.h2.tools.Server h2WebConsonleServer () throws SQLException {
return org.h2.tools.Server.createWebServer("-web","-webAllowOthers","-
webDaemon","-webPort", "8082");
}
#Bean
public DataSource getDataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(false)
.setName("mytestdb")
.setType(EmbeddedDatabaseType.H2)
.addDefaultScripts()
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.build();
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setHibernateProperties(hibernateProperties());
sessionFactory.setPackagesToScan(new String[] {"org.testpackage.model"});
return sessionFactory;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(final SessionFactory sessionFactory) {
final HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
final Properties hibernateProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "validate");
hibernateProperties.setProperty("hibernate.show_sql", "true");
return hibernateProperties;
}
//Some more beans
}
I have the following Entity class
#Entity
#Table(name = "MYTESTDB.TEST_TABLE")
public class User{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "email", unique = true)
private String email;
public User(int id, String name, String email) {
super();
this.id = id;
this.name = name;
this.email = email;
}
public User() {
}
//Getters and Setters
}
in the DataSource bean I am using addDefaultScripts() and I have 2 sql scripts which create Schema in H2 and insert some predefined value. Which are as follows.
//schema.sql Script
CREATE SCHEMA `MYTESTDB` ;
Drop TABLE IF EXISTS MYTESTDBDB.TEST_TABLE;
CREATE TABLE MYTESTDB.TEST_TABLE (
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
EMAIL VARCHAR(20) NOT NULL,
);
CREATE UNIQUE INDEX ON MYTESTDB.TEST_TABLE (EMAIL)
//data.sql Script
INSERT INTO MYTESTDB.TEST_TABLE(id, name, email)
VALUES ('1', 'Tom', 'tom12#hotmail.com');
If I use hibernate hbm2ddl.auto property value "create" everything works fine, hibernate drops the table and recreate it. I have verified it from the web browser. But if I use "validate" property I get the following error exception
Error creating bean with name 'sessionFactory' defined in org.testPackage.configuration.MYConfiguration: Invocation of init method failed; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [MYTESTDB.TEST_TABLE]
Can anyone please help me to find out the problem?
Its working now. With the help of #Slaw I am able to use "Validate" property.
Use #Table(schema = "MYTESTDB", name = "TEST_TABLE") instead of #Table(name = "MYTESTDB.TEST_TABLE"). But I had to change a bit more in user entity class. Instead of #GeneratedValue annotation I added #GeneratedValue(strategy = GenerationType.IDENTITY). Now everything is working fine.
Thanks #Slaw and #Mykhailo for your valuable time.
I have two entities, Branch and Enterprise, the two entities extends of a AbstractEntity
AbstractEntity
#Id
#Column(length=36)
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
Branch entity:
private String name;
private int code;
#ManyToOne
#JoinColumn(name = "enterprise_id")
private Enterprise enterprise;
Enterprise entity
private String name;
private String nit;
private String sheetconfig;
#OneToMany(cascade = CascadeType.ALL,
mappedBy = "enterprise", orphanRemoval = true)
private List<Branch> branches = new ArrayList<Branch>();
When i try to save a branch, i select the enterprise in a combobox and send the request but i get the follow error:
object references an unsaved transient instance - save the transient instance before flushing : com.gettecob.gestcart.model.Branch.enterprise
NOTE: The enterprises list exist, only i need save the branch whit the enterprise assoc.
The bean config:
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DriverManagerDataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setPackagesToScan(new String[]{"com.gettecob.gestcart.*"});
entityManagerFactoryBean.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Map<String, Object> jpaProperties = new HashMap<String, Object>();
jpaProperties.put("hibernate.hbm2ddl.auto", "create");
jpaProperties.put("hibernate.show_sql", "true");
jpaProperties.put("hibernate.format_sql", "true");
jpaProperties.put("hibernate.use_sql_comments", "true");
jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
return entityManagerFactoryBean;
}
The BranchRepository save method
public Branch save(Branch branch) {
return em.merge(branch);
}
I need help whit this.
The reason why Enterprise is unmanaged is not clear to me. It might be not persisted at all or it might be detached or obtained from some other persistence context.
It can happen for example if you set the id of the new entity to imitate existing entity since persistence context should handle setting id.
If it is not persisted before then in order to have Enterprise persisted when persisting Branch you need to enable CascadeType.PERSIST in its #ManyToOne annotation
#ManyToOne(cascade=CascadeType.PERSIST)
#JoinColumn(name = "enterprise_id")
private Enterprise enterprise;
You can try also to enable CascadeType.MERGE. It depends a bit on what reason your Enterprise is considered as detached.
There are two entities: parent and child one.
#Entity
#Table(name = "university_group")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String name;
#OneToMany(mappedBy = "group", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Student> students = new HashSet<>();
// getters, setters, constructor, equals+hashcode ...
}
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String name;
private String password;
private String email;
#ManyToOne(optional = false)
private Group group;
// getters, setters, constructor, equals+hashcode ...
}
After removing group by em.remove(group) an exception is thrown:
javax.persistence.PersistenceException:
org.hibernate.exception.ConstraintViolationException ...
org.hibernate.engine.jdbc.spi.SqlExceptionHelper: ERROR:
UPDATE or DELETE in table "university_group" breaks foreign key constraint "fk_20su8ubuwt33je1a3ygal7wd6" of table "student"
It seems like hibernate is not deleting students before the group by means of Persistence Provider although it should. Of course, I am able to enable DB cascading, but I would better solve the problem.
Any ideas?
Configured the EntityManager by Spring configs
#Configuration
#EnableTransactionManagement
#PropertySource({"classpath:db.properties"})
public class PersistenceContext {
private static final String BASE_MODEL_SCAN_PACKAGE = "com.chiefhelpsystem.model";
#Value("${db.driverClassName}")
private String dbClassName;
#Value("${db.url}")
private String dbUrl;
#Value("${db.username}")
private String dbUserName;
#Value("${db.password}")
private String dbPassword;
#Bean
DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setMaxIdle(20);
ds.setMinIdle(0);
ds.setMaxActive(20);
ds.setDriverClassName(dbClassName);
ds.setUrl(dbUrl);
ds.setUsername(dbUserName);
ds.setPassword(dbPassword);
return ds;
}
#Bean
PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
#Bean(destroyMethod = "destroy")
LocalContainerEntityManagerFactoryBean emf() {
LocalContainerEntityManagerFactoryBean emFactory =
new LocalContainerEntityManagerFactoryBean();
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabase(Database.POSTGRESQL);
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setShowSql(true);
emFactory.setDataSource(dataSource());
emFactory.setPackagesToScan(BASE_MODEL_SCAN_PACKAGE);
emFactory.setJpaVendorAdapter(jpaVendorAdapter);
emFactory.setJpaProperties(jpaProps());
emFactory.setPersistenceProvider(new HibernatePersistenceProvider());
return emFactory;
}
private Properties jpaProps() {
Properties properties = new Properties();
properties.setProperty("format_sql", "true");
return properties;
}
}
Hibernate 4.3.11, Spring 4.3.2
The problem was in the incorrect hachcode() method realization. As soon as I deleted it from sources, the "un-managed deleting" problem has appeared in hibernate TRACE logs and it should be further fixed.