How to create insert statements for table with Collection - sql

I have a Spring boot JPA project that has an entity Entitya. This entity has a collection attribute
#ElementCollection(targetClass=String.class)
private List<String> options = new ArrayList<String>(10);
with get/set
I see tables created - Entitya and Entitya_options [looks like entitya doesnt know anything about Entitya_options. But entitya_options has foreign key to Entitya]
I don't need the extra table, but it's ok
I want to insert data using import.sql, how can I do?
Insert into "ENTITYA" (FORMULA,NOTE,ACTION_ID) values (....);
How to insert Entitya_options too , when inserting Entitya ?
EDIT
Question:
Should I use the below annotations? as mentioned - How to persist a property of type List<String> in JPA?
#ElementCollection // 1
#CollectionTable(name = "my_list", joinColumns = #JoinColumn(name = "id")) // 2
#Column(name = "list") // 3
private List<String> list;
I need complete insert statements, that can be put in import.sql

You can use import.sql (Hibernate support) or data.sql (Spring JDBC support) files to load data.
Ff you use JPA and an embedded datasource, your schema.sql won’t be taken into effect, because Hibernate’s DDL generation has priority over it.
A solution to that problem is to set the following property:
spring.jpa.hibernate.ddl-auto=none
Since Spring boot 2, the schema is only initialized by default for embedded datasources. To allow loading data for all types of datasources, you have to set the following property:
spring.datasource.initialization-mode=always
spring.datasource.data=classpath:my_script1.sql, classpath:my_script2.sql
Update
#Component
public class MyEntityInitializer implements CommandLineRunner{
#Autowired
EntityaRepository entityaRepository;
#Override
public void run(String... args) {
Entitya entitya = new Entitya();
entitya.setOptions(Arrays.asList("op1","op2"));
entityaRepository.save(entitya);
}
}
By this you can your entries inserted every time your application is started.
Make sure to check for already existing records, otherwise you get primary key exception.

Related

How to construct Spring Data repository query two Parameters with IN and same list?

This is my Entity:
#Data
#Entity
#IdClass(EtlJobExecutionTriggersId.class)
#Table(name = "ETL_JOB_EXEC_TRIGGERS")
public class EtlJobExecutionTriggers {
#Id private Long jobExecIdUs;
#Id private Long jobExecIdDs;
private LocalDate cobDate;
}
And here is the Composite Primary Key Class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
#EqualsAndHashCode
public class EtlJobExecutionTriggersId implements Serializable {
private Long jobExecIdUs;
private Long jobExecIdDs;
}
And here is my Spring Repo:
public interface EtlJobExecTriggersRepo extends JpaRepository<EtlJobExecutionTriggers, EtlJobExecutionTriggersId> {
String SQL_ = "select o from EtlJobExecutionTriggers o where o.jobExecIdDs in (:ids) or o.jobExecIdUs in (:ids) order by o.jobExecIdUs, o.jobExecIdDs";
#Query(EtlJobExecTriggersRepo.SQL_)
List<EtlJobExecutionTriggers> findAllByJobExecIdDsInAndJobExecIdUsInSQL(#Param("ids") List<Long> jobExecIdList);
}
The #Query works as expected, but I would like not to write any SQL and instead express the same Query using only Spring Data repository query.
I have tried the following (and other variants)
List<EtlJobExecutionTriggers> findAllByJobExecIdDsInAndJobExecIdUsInOrderByJobExecIdUsJobExecIdDs(List<Long> jobExecIdDsList)
But i keep getting errors when Booting. The above interface method yields the following exception for the OrderBy part:
org.springframework.data.mapping.PropertyReferenceException: No property jobExecIdDs found for type Long! Traversed path: EtlJobExecutionTriggers.jobExecIdUs.
So what am I doing wrong here? or is it not possible to express this particular query via Spring Data Repo query?
As I have written in my comment I fixed the Order by issue, but I am still unable to make it work with only one method parameter (List jobExecIdList)
When I make it with two (List jobExecIdDsList, List jobExecIdUsList)
Like this:
List<EtlJobExecutionTriggers> findAllByJobExecIdDsInAndJobExecIdUsInOrderByJobExecIdUsAscJobExecIdDsAsc(List<Long> jobExecIdDsList, List<Long> jobExecIdUsList);
it actually works but I can't get to work with only one list, as in the #Query("....") method
I think using your own custom id generator conflicts with Spring Data Repository query.
// You shoud have two parameters in your method as below.
List findAllByJobExecIdDsInAndJobExecIdUsInOrderByJobExecIdUsJobExecIdDs(List jobExecIdDsList,List jobExecIdUsList);

Hibernate manual and auto-generated primary key

I am having a requirement where if the user enters value for the primary key, then I need to use that when creating an entity and if in case the user does not provide value, the primary key needs to be auto-generated like R00001, R0002 etc.I would like to know how I could achieve this and any guidance on that
Try to take advantage of the IdentifierGenerator interface and define an implementation of your own.
public class MyEntityIdGenerator implements IdentifierGenerator{
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
MyEntity entity = (MyEntity)object;
if(entity.getId()==null){
Connection con = session.connection();
// retrieve next sequence val from database for example
return nextSeqValue;
}
}
}
Then add appropriate annotations on the id field in your entity:
#Id
#GenericGenerator(name="myCustomGen", strategy="com.example.MyEntityGenerator")
#GeneratedValue(generator="myCustomGen")

Best way to set transient attribute on entity instance after Metadata.create

I'm currently setting the result of a jpql query on a transient attribute of several instances of entities attached with composition using BeforeDetachEntityListener.
Since I'm also using Metadata.create to create them, I would like to be able to do the same operation after creating them. What's the best way to handle the situation?
You can set values at object creation time with #PostConstruct
public class MyEntity extends StandardEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID")
protected User creator;
#PostConstruct
protected void init() {
setCreator(AppBeans.get(UserSessionSource.class).getUserSession().getUser());
}
}
More information about how to initialize data in entities can be found in the docs at 5.8.3.1 Entity Fields Initialization and 5.8.3 Assigning Initial Values

Fluent nHibernate SubclassMap and AddFromAssemblyOf

I created a generic user repository base class that provides reusable user management functionality.
public class UserRepository<TUser> where TUser : new, IUser
{
}
I have a concrete implementation of IUser called UserImpl, and corresponding mapping class UserImplMap : ClassMap<UserImpl> (they all are in the same namespace and assembly). I add the mapping using AddFromAssemblyOf . I also use this to create / generate the schema.
So far so good and things work as expected.
Now, in a different project, I needed a few additional properties in my IUser implementation class, so I implemented a new class UserImplEx : UserImpl. This class has the additional properties that I needed. Also, I created a new mapping class UserImplExMap : SubclassMap<UserImplEx>
Now when I create schema using this approach, I get two tables one for UserImpl and one for UserImplEx.
Is is possible to configure / code Fluent mapping in some way so that all the properties (self, plus inherited) of UserImplEx get mapped in a single table UserImplEx instead of getting split into two tables?
Alternatively, if I provide full mapping in UserImplExMap : ClassMap<UserImplEx>, then I do get the schema as desired, but I also get an additional table for UserImpl (because corresponding mapping is present in the UserRepository assembly). If I follow this approach, is there a way to tell AddFromAssemblyOf to exclude specific mapping classes?
Option 1
since you have inhertance here and want the correct type back NH has to store the type somewhere, either through the table the data is in or a discriminator.
If a discriminator column in the table does not matter then add DiscriminatorColumn("userType", "user"); in UserImplMap and DiscriminatorValue("userEx") in UserImplExMap
Option 2
class MyTypeSource : ITypeSource
{
private ITypeSource _inner = new AssemblyTypeSource(typeof(UserImplMap).Assembly);
public IEnumerable<Type> GetTypes()
{
return _inner.Where(t => t != typeof(UserImplMap)).Concat(new [] { typeof(UserImplExMap) });
}
public void LogSource(IDiagnosticLogger logger)
{
_inner.LogSource(logger);
}
public string GetIdentifier()
{
return _inner.GetIdentifier();
}
}
and when configuring
.Mappings(m =>
{
var model = new PersistenceModel();
PersistenceModel.AddMappingsFromSource(new MyTypeSource());
m.UsePersistenceModel(model);
})

NHibernate query by Transient instance results in "save the transient instance"-exception

I have some old code which is performing a query where a model can be transient. That is, a model with some fields populated from user input, which are then used as part of the query.
It worked under NH 2.1.x, but is failing under the latest version.
The exception raised is "object references an unsaved transient instance - save the transient instance before flushing". This happens when NH attempts to perform a query using a non-persisted object as part of the query.
A simplified version to illustrate the problem.
abstract class BaseModel
public virtual long Id { get; set; }
class Car : BaseModel
public virtual Engine Engine { get;set; }
class Engine : BaseModel
public virtual string Kind { get; set; }
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>();
c.Add(Expression.Eq("Engine", eng));
return c.List<Car>(); // <--- Error occurs here
}
And calling code is equivalent to this:
Engine obj = new Engine { Id = 42 }; // Transient instance
var x = GetByEngine(obj);
What I expected to happen (Which appears to be the behaviour of the old NHibernate version), is that the Engine passed is used only for getting the Id. That is, generating SQl like
select .... from Cars where Engine = 42
But with the new version NHibernate seems to check that the engine used in the Expression is actually persisted.
Is there a way to avoid having to load a persisted Engine before performing the query ?
yes using Session.Load() which returns the object if already in the session or a lazyLoadingProxy if not present.
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>();
c.Add(Expression.Eq("Engine", Session.Load<Engine>(eng.Id)));
return c.List<Car>();
}
You can use the Session.Load method, which exist for this kind of scenarios.
The Load method will return a Proxy to the Entity and won't hit the Data Base untill you access one of it's properties, (except the Primary key property which won't hit the DB at all).
Usage:
Engine obj = session.Load<Engine>(42);
var x = GetByEngine(obj);
check this article about Session.Get and Session.Load
I think you could do something like this:
public static IList<Car> GetByEngine(Engine eng) {
ICriteria c = Session.CreateCriteria<Car>().CreateCriteria("Engine");
c.Add(Expression.Eq("Id", eng.Id));
return c.List<Car>();
}
Anyway... how it's possible that a car with that engine exists if you haven't saved it yet?