Hibernate manual and auto-generated primary key - sql

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")

Related

How to create insert statements for table with Collection

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.

Implementing GUID in Yii

If you have a data model in which one table is a guid table with just a guid column, and many tables have a primary key referencing that guid, how would you recommend incorporating this type of logic into Yii? To create a new model of any guidable thing, you have to create a guid first. Where is the right place to put this sort of logic?
Edit: To be more specific, here is the issue I am facing:
I have a table of guids, tbl_guid, with one column guid that is a MySQL BIGINT
Some tables, like tbl_foo, have a PK guid referencing guid in tbl_guid
The Foo model has the relation self::BELONGS_TO, 'Guid', 'guid'
I do not want to create a new guid until I am definitely ready to create Foo
I'd like to somehow delay the saving of my guid until I'm actually saving (and have otherwise validated) Foo
However, Foo never validates, because it doesn't have a guid.
Edit 2: I've posted my own answer, but I am hoping somebody has a better answer/improvement to suggest. Here are the issues with my answer:
How can we force the owner to comply with some interface, so we don't have to throw in a bunch of conditionals to check for whether or not the owner has an attribute or method, etc
Even though the database will not accept null for guid, it still seems wrong to remove guid from the list of required attributes.
This is the best I've been able to come up with:
Create a new CActiveRecordBehavior for guidable models:
public function beforeSave() {
if (!$this->owner->guid) {
$guid = new Guid;
$guid->save();
$this->owner->guid = $guid->guid;
}
}
Attach the behavior on the model, or define it in the model's behaviors array.
public function init() {
$behavior = new GuidBehavior;
$this->attachBehavior('GuidBehavior', $behavior);
}
Remove the required-ness of guid so validation doesn't fail:
array('name', 'required'), //guid isn't here
Test
$brand->save();
Inheritance. If you implemented my BaseModel code a while ago, you can override the __construct() method in the BaseModel to create an instance of the GUID class.
BaseModel:
public function __construct()
{
parent::__construct();
$newGuid = new GUID();
return $this;
}
See http://www.yiiframework.com/doc/api/1.1/CActiveRecord

NHibernate many-to-one relationship

We have the following Domain objects :-
public class UserDevice : BaseObject
{
// different properties to hold data
}
public class DeviceRecipient:BaseObject
{
public virtual UserDevice LastAttemptedDevice{get;set;}
}
Hence the sql schema created based on this using fluent nhibernate automapper is like
DeviceRecipient's table is having primary key of UserDevice as a foreign key i.e UserDevice_Id.
Now, When we try to delete UserDevice object it gives a sql exception for foreign key constraint. What we want to do is :-
Delete the UserDevice object , hence the UserDevice row without deleting the DeviceRecipient as it will be used somewhere else in domain model. We just want to set null to UserDevice_Id column of DeviceRecipient when we delete UserDevice.
We want to do it using fluent nhibernate conventions as we use Automapping.
Any help will be appreciable.. Thanks in advance.!
As I can see you have uni-direction many-to-one relation. So firstly you have to write following override:
public class DeviceRecipientOverride : IAutoMappingOverride<DeviceRecipient>
{
public void Override(AutoMapping<DeviceRecipient> mapping)
{
mapping.References(x => x.LastAttemptedDevice)
.NotFound.Ignore(); // this doing what you want.
}
}
Secondly you could convert it to automapping convention, if you have more places with this behavior.
public class ManyToOneNullableConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
var inspector = (IManyToOneInspector) instance;
// also there you could check the name of the reference like following:
// inspector.Name == LastAttemptedDevice
if (inspector.Nullable)
{
instance.NotFound.Ignore();
}
}
}
EDIT:
From the NHibernate reference
not-found (optional - defaults to exception): Specifies how foreign
keys that reference missing rows will be handled: ignore will treat a
missing row as a null association.
So when you set not-found="ignore" SchemaExport/SchemaUpdate will just not create the FK for you. So if you have the FK then you need to delete it or set OnDelete behavior of the FK to Set Null. Assuming that you are using Microsoft Sql Server:
ALTER TABLE [DeviceRecipient]
ADD CONSTRAINT [FK_DeviceRecipient_LastAttemptedDevice]
FOREIGN KEY ([LastAttemptedDevice_ID])
REFERENCES [UserDevice]
ON DELETE SET NULL

Composite primary key declaration through Convention for fluent-nHibernate

I need to use Fluent-nHibernate against a table with a composite primary key (Azure Table, primary keys being (PartitionKey,RowKey) and I would like to map them with corresponding properties on the entity (or with a component property, if easier)
my table would look like:
{
PartitionKey PK,
RowKey PK,
[..]
}
and the entity
public class MyRecord
{
public virtual string PartitionKey{get;set;}
public virtual string RowKey{get;set;}
[...]
}
My current projet uses a custom nHibernate Driver targeting AzureTable.
I managed to make it work with ClassMap or XML mappings. Therefore I am sure that the driver is working. Furthermore, the azure table HTTP requests are correct using classmaps or XML declarations.
However I really need conventions, so this isn't an acceptable solution.
Finally, there is always the option to map only RowKey as a PK, even if the Datastore use (PartitionKey,RowKey). It works too, However it's not really satisfying as it introduces an unicity handling mismatch between nHibernate and the underlying datastore.
UPDATE:
I tried to build a custom IIdentityConvention. The IIdentityInstance.Column() method takes into account only the first call.
However, if I use reflection to add both columns to the underlying mapping field, the configuration build fails with an XML validation exception (attribute 'class' required)
I got it working today, but it's not pretty. It also doesn't use a convention. As I understand conventions, they're really meant for tweaking things after the main mapping has occurred. Adding mappings I believe is considered out of scope for conventions.
In my project I have a generic automapping-based initialization procedure that knows nothing of types, but has dependency-injected mapping overrides for composite keys. Not exactly your scenario, but it's a similar problem.
The way I got this to work through reflection was to get hold of the appropriate AutoPersistenceModel object. If you have code looking like this:
Fluently.Configure().Mappings(m => ...
The AutoPersistenceModel object would be m.AutoMappings.First()
From here, it's pretty serious reflection work, culminating in a call to a protected method inside FluentNHibernate. Here's the code I'm using:
private void Override(AutoPersistenceModel container,
Type type,
IEnumerable<KeyValuePair<string,string>> compositeKeys)
{
// We need to call container.Override<T>(Action<Automapping<T>> populateMap)
// Through reflection...yikes
var overrideMethod = typeof(AutoPersistenceModel)
.GetMethod("Override")
.MakeGenericMethod(type);
var actionFactoryMethod = typeof(FluentNHibernateInitializer)
.GetMethod("CompositeMapperFactory",
BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(type);
var actionMethod = actionFactoryMethod
.Invoke(this, new object[] { compositeKeys });
overrideMethod.Invoke(container, new object[] {actionMethod});
}
private Action<AutoMapping<T>> CompositeMapperFactory<T>
(IEnumerable<KeyValuePair<string, string>> compositeKeys)
{
return new Action<AutoMapping<T>>(m =>
{
var compositeId = m.CompositeId();
foreach (var kvp in compositeKeys)
compositeId =
AddKeyProperty(
compositeId,
typeof(T).GetProperty(kvp.Key),
kvp.Value);
}
);
}
/// <summary>
/// Uses reflection to invoke private and protected members!
/// </summary>
/// <param name="compositeId"></param>
/// <param name="propertyInfo"></param>
/// <returns></returns>
private CompositeIdentityPart<T> AddKeyProperty<T>
(CompositeIdentityPart<T> compositeId,
PropertyInfo propertyInfo,
string column)
{
var member = FluentNHibernate.MemberExtensions.ToMember(propertyInfo);
var keyPropertyMethod = typeof(CompositeIdentityPart<T>)
.GetMethod("KeyProperty",
BindingFlags.Instance | BindingFlags.NonPublic);
return (CompositeIdentityPart<T>)
keyPropertyMethod
.Invoke(compositeId, new object[] { member, column, null });
}

saveOrUpdate(Object) method of Hibernate

when i use saveOrUpdate(Object) method of Hibernate. How can I know that row is updated or new row added into table??? Return type of method saveOrUpdate(Object) is void, so am not able to find out the result after calling this method.
kindly help me.
As I understand from your question and comment lived. You could create an event listener and implement two Interfaces: IPreUpdateEventListener, IPreInsertEventListener
e.q.
public class AuditEventListener : IPreUpdateEventListener, IPreInsertEventListener
{
public bool OnPreUpdate(PreUpdateEvent #event)
{
//your stuff here
return false;
}
public bool OnPreInsert(PreInsertEvent #event)
{
//your stuff here
return false;
}
}
but I think this is ridiculous. using ORMs means that you do not care about persistence and all work is done the unitofwork. If you really need to insert and update just use Save() or Update() methods, in this way you will exactly know what operation is made.
If your object to be persisted is having identifier property set to 0(/id null) then it means it is a new object and will be inserted newly in db.
After it is inserted hibernate will then set the id value in identifier field.
If already the object has identifier property set then it means the object is already persisted and it can be updated
EDIT:Did you look at hibernate interceptors?May be this is useful.
example