get auto-genearated key for the inserted record in mybatis - spring-mvc

I am working under a web application which use spring mvc + mybatis + mysql.
And I found that I can not get the auto-generated key for the last inserted record (I have googled so much).
This is the related configuration(take the model 'Post' for example):
spring.xml:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
//omitted
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="config.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
mybatis config.xml
<configuration>
<typeAliases>
<typeAlias alias="Post" type="com.king.model.Post" />
</typeAliases>
<mappers>
<mapper resource="com/king/model/PostMapper.xml" />
</mappers>
</configuration>
PostMapper.xml:
<mapper namespace="com.king.model.PostMapper">
<insert id="insert" parameterType="Post">
insert into posts (title,body,created_at,updated_at) values (#{title},#{body},#{createDate},#{updateDate})
</insert>
</mapper>
Dao:
public abstract class AbstractSimpleDaoImpl<T> extends SqlSessionDaoSupport{
#Override
public int add(T entity) {
return getSqlSession().insert(getMapperNamespace() + ".insert", entity);
}
protected abstract String getMapperNamespace();
}
public class PostDao extends AbstractSimpleDaoImpl<Post> {
#Override
protected String getMapperNamespace() {
return "com.king.model.PostMapper";
}
}
However,when I insert a new Post to database:
postDao.add(post);
I can not get the generated id for the inserted post. I always get 1. I know this is the row-affected number.
Also,I read the mybatis guide,and I tried this:
<insert id="insert" parameterType="Post" useGeneratedKeys="true" keyProperty="id">
But it seems that,this does not work.
How to fix it?

In fact the following works:
<insert id="insert" parameterType="Post" useGeneratedKeys="true" keyProperty="id">
postDao.add(post);
will return 1 as before,but post.getId() will get the key.

This can be done using annotations as well.
final String INSERT = "insert into posts (title,body,created_at,updated_at) values (#{title},#{body},#{createDate},#{updateDate})";
#Insert(INSERT)
#Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
void insert(Post post) throws Exception;
Set keyProperty as the Java variable name.
Set keyColumn as the column name in the database.
After insert, post.getId() will have your generated ID.

Try using the following code; I added the keyColumn field to your code:
<insert id="insert" parameterType="Post" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
Alternatively, if you are using PostgreSql:
<insert id="insert" parameterType="Post" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
<selectKey keyProperty="id" resultType="java.lang.Integer">
SELECT
currVal('your_tbl_seq') as id
</selectKey> </insert>

This will work for Spring Boot using annotations and getting the id as a return value as in your original question.
#Insert("insert into sources (host, created_at) values (#{host}, now())")
#SelectKey(statement="select LAST_INSERT_ID()", keyProperty="id", before=false, resultType=long.class)
long create(#Param("host") String host);
Then long id = sourceMapper.create("abc"); would return your primary key.

Related

How to set visibility for actions for specific user groups in Alfresco

I am new in Alfresco, and currently I am working on small project. The problem that I have is that I need to set one action from multi-select to be visiable just for one user group. This action is on Search window, and it's download action.
(Picture attached)
I read that we can use Evaluators, but I am not sure how to do that.
This is my code inside xml:
<config evaluator="string-compare" condition="DownloadAll">
<multi-select>
<action type="action-link" id="onDownloadAllDocumentAsZip" icon="document-download"
label="action.download.all.as.zip.label" />
<action type="action-link" id="onDownloadAllAsExcel" icon="document-download"
label="action.download.all.as.excel.label" />
<action type="action-link" id="onDownloadAllStudyNotificationAsExcel" icon="document-download"
label="action.download.all.study.notification.as.excel.results.label" />
<action type="action-link" id="onDownloadAllAsExcelIUCLID" icon="document-download"
label="action.download.all.as.excel.iuclid.label" />
<action type="action-link" id="onDownloadAllAsExcelUpdateMetadata" icon="document-download"
label="action.download.all.as.excel.update.metadata.label" />
</multi-select>
</config>
I would have to introduce Evaluators or something like that on last action inside this multi-select.
Thanks in advance!
Yes, evaluator in share-config-custom.xml is the right way. Check some documentation https://docs.alfresco.com/content-services/latest/develop/share-ext-points/evaluators/. Simply you add tag <evaluator> inside <action> for example:
<action type="action-link" id="onDownloadAllAsExcelUpdateMetadata" icon="document-download" label="action.download.all.as.excel.update.metadata.label">
<evaluator>my.evaluator.doclib.action.isAdmin</evaluator>
</action>
Name of evaluator have to be set properly set in file /share/src/main/resources/alfresco/web-extension/yourprefix-share-slingshot-application-context.xml.
Take inspiration in this file with default settings https://github.com/Alfresco/share/blob/master/share/src/main/resources/alfresco/slingshot-documentlibrary-context.xml You can make lot of combinations via Predefined evaluators https://docs.alfresco.com/content-services/latest/develop/reference/share-document-library-ref/ If you wont be able make evaluator for specific group, write own java in path
share/src/main/java/your_packages/share/web/extesibility. This is how look my IsAdminEvaluator:
public class IsAdminEvaluator extends org.alfresco.web.evaluator.BaseEvaluator
{
public IsAdminEvaluator() {}
public boolean evaluate(JSONObject jsonObject)
{
RequestContext rc = org.springframework.extensions.surf.support.ThreadLocalRequestContext.getRequestContext();
User user = rc.getUser();
return (user != null) && (user.isAdmin());
}
}
and bean in yourprefix-share-slingshot-application-context.xml.
<bean id="my.evaluator.doclib.action.isAdmin" class="your_packages.share.web.extesibility.IsAdminEvaluator" />
If you need call some repo webscript use this.
final RequestContext rc = ThreadLocalRequestContext.getRequestContext();
final Connector conn = rc.getServiceRegistry().getConnectorService().getConnector("alfresco", rc.getUserId(), ServletUtil.getSession());
// Get nodeRef
final String nodeRef = (String) jsonObject.get("nodeRef");
if (nodeRef == null) {
return false;
}
// Create a Pattern object
Pattern r = Pattern.compile("^(.*):\\/(\\/.*)(\\/.*)$");
// Create matcher object
Matcher m = r.matcher(nodeRef.toString());
final Response response = conn.call("/signia/parent?nodeRef=" + m.group(0));
So in the worst way you can call /people/{personId}/groups webscript and look if users is in your group. But I think you can make solution from Predefined evaluators. I hope this help you ;)

Update DB via EntityManager

I'm working on a Spring-MVC project, trying to update a user's username and password using jpa. But I don't seem to be making any progress. :D
I checked other questions like this one and tried to use their approved answers for my case. Alas, nothing changes in my db.
By far, I have tried these methods and none has worked:
using persist before set, as suggested here
using merge after set as suggested here
commiting a transaction manually as suggested here.
Unfortunately, this class has a lot of foreign keys from all around my DB, and I can't safely remove the old one and add a new Dept with new data in it. So, here goes my update method -in DAO level of course- would someone please suggest a new way to update username and password here?
#Transactional
public Dept updateDept(int depId, String u, String p) {
System.err.println("successfully reached DAO");
Dept dep=entityManager.find(Dept.class,depId);
dep.setUname(u);
dep.setPassword(p);
entityManager.merge(dep);
return entityManager.find(Dept.class,depId); //just to check if update really happened, which did not
}//end of update Dept
edit:
Ok, so it is getting really complicated. I'm really a newbie in this area, so I try to give you as much detail as possible.
These are the only parts I was allowed to change, and I have to consider that any other configuration to other parts like JPA etc. is done by my senior team mate and is done correctly.
This is my controller which is supposed to update username and password of department manager, and send user back to their first page:
#RequestMapping(value="/infoEdit", method=RequestMethod.POST)
public String editManager(HttpServletRequest req, #RequestParam("username") String usernname , #RequestParam("password") String password , Model model) {
int id= (Integer) req.getSession().getAttribute("id");
boolean isUpdated=managerManagerImpl.updateUserPass(id,usernname,password);
System.err.println(isUpdated);
Dept d= managerManagerImpl.getDeptByManagerId(usernname,password);
if (d!=null)
return "themanager/managerFirstPage";
}
In managerManagerImpl - implementation of managerManager interface , updateUserPass() goes like this:
#Override
public Boolean updateUserPass(int id, String usernname, String password) {
Dept dept=managerDAOImpl.getDept(id); // who is this?
Dept possibleDup=managerDAOImpl.getDept(usernname);//maybe this username is taken
Dept newDep=null;
if(possibleDup==null || (possibleDup!=null && possibleDup.getId()==dept.getId())){//username is not taken
System.err.println("going to update it!!");
newDep=managerDAOImpl.updateDept(dept.getId(),usernname,password);
System.err.println("newDep data here : " + newDep.getId() + "// "+newDep.getUname());
Dept newnewDept = managerDAOImpl.getDept(id);
if(newnewDept!=null)
System.err.println("&& newnewDep data here : " + newnewDept.getId() + "// "+newnewDept.getUname());
}
Boolean isUpdated=(null!=newDep && newDep.getId()==dept.getId());
return isUpdated;
}
and this is my whole managerDAOImpl class:
#Repository
public class TheManagerDAOImpl {
public TheManagerDAOImpl() {
}
#PersistenceContext
public EntityManager entityManager;
#Transactional
public Prof getManager(String usern){
String hql="SELECT p FROM Dept p WHERE p.uname=:username";
Query q= entityManager.createQuery(hql);
q.setParameter("username",usern);
List<Prof> res = (List<Prof>) q.getResultList();
return res == null || res.size() == 0 ? null : res.get(0);
}
#Transactional
public Dept getDept(int managerId) {
String hql= "SELECT d FROM Dept d WHERE d.id=:idHere";
Query q= entityManager.createQuery(hql);
q.setParameter("idHere",managerId);
List<Dept> res = (List<Dept>) q.getResultList();
return res == null || res.size() == 0 ? null : res.get(0);
}
#Transactional
public Dept getDept(String managerId, String managerPass) {
String hql= "SELECT d FROM Dept d WHERE d.uname=:username AND d.password=:pass";
Query q= entityManager.createQuery(hql);
q.setParameter("username",managerId);
q.setParameter("pass",managerPass);
List<Dept> res = (List<Dept>) q.getResultList();
return res == null || res.size() == 0 ? null : res.get(0);
}
#Transactional
public Dept getDept(String managerId) {
String hql= "SELECT d FROM Dept d WHERE d.uname=:username";
Query q= entityManager.createQuery(hql);
q.setParameter("username",managerId);
List<Dept> res = (List<Dept>) q.getResultList();
return res == null || res.size() == 0 ? null : res.get(0);
}
#Transactional
public Dept updateDept(int depId, String u, String p) {
System.err.println("**************************here in dao");
Dept dep=entityManager.find(Dept.class,depId);
dep.setUname(u);
dep.setPassword(p);
entityManager.merge(dep);
return entityManager.find(Dept.class,depId);
}//end of update Dept
}
Just to mention, those System.error stuff are used to make sure it actually calls these methods, and are completely irrelevant.
You are getting container-manager persistence context, which has to get JTA datasource, but as I saw before(you have already removed it) from your persistence.xml, you are using resource local transactions (by specifying jdbc resource).
Correct configuration should be (for XML config)
1) Your datasource
<jdbc:embedded-database id="dataSource" type="H2">
</jdbc:embedded-database>
// or jdbc:initialize-database for non-embedded
2) Your transaction manager
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
</bean>
2.1) As you are using annotation to do transaction demarcation you need
<tx:annotation-driven transaction-manager="transactionManager" />
3) Your entity manager factory
bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="com.myPackage"/>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
I found another way to do updates, which I'm not sure if it has any drawbacks, or it would blow my code up in the future. But since it solved this issue for now, I share it here. Maybe it will help.
For now I clear my entityManager before updating any existing entity. Somehow like this:
#Transactional
public void updateDept(Dept dep,String username, String password) {
entityManager.clear();
dep.setUname(username);
dep.setPassword(password);
entityManager.merge(dep);
}//end of update Dept
Surprisingly, it now works fine and commits updates on DB.

Encrypted Oracle-Ds.xml and decryption in jdbc code

Server :JBOSS 5.x
Following function is written in my session bean(ejb3 architecture)
#Remote(OracleDsInteractionRemote.class)
#Local(OracleDsInteractionLocal.class)
#Stateless
#RemoteBinding(jndiBinding="OracleDsInteractionBean/remote")
#LocalBinding(jndiBinding="OracleDsInteractionBean/local")
public class OracleDsInteractionBean implements OracleDsInteractionRemote,OracleDsInteractionLocal
{
#EJB
#Resource(mappedName="java:/EncryptedDS")
private DataSource ds1;
public int getAllValues() throws Exception
{
System.out.println("************************* ");
Connection conn = ds1.getConnection();
Statement st = conn.createStatement();
String query = "SELECT count(*) FROM DETAILS_DATA";
ResultSet rs = st.executeQuery(query);
while (rs.next())
{
return rs.getInt(1);
}
return -1;
}
}
This function is retrieving number of rows from details_data table using jdbc connection. DataSource Object ds1 is mapped with 'EncryptedDS'. Entry of 'EncryptedDS' is defined in oracle-ds.xml as following.
<?xml version="1.0" encoding="UTF-8"?>
<!-- ===================================================================== -->
<!-- -->
<!-- JBoss Server Configuration -->
<datasources>
<xa-datasource>
<jndi-name>EncryptedDS</jndi-name>
<xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
<xa-datasource-property name="URL">jdbc:oracle:thin:#182.158.93.26:1521:dbSid</xa-datasource-property>
<xa-datasource-property name="User">Test</xa-datasource-property>
<xa-datasource-property name="Password">pass123</xa-datasource-property>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>Oracle11g</type-mapping>
</metadata>
</xa-datasource>
</datasources>
But problem is that anyone can go to $JBOSS_HOME/server/default/deploy directory and find out 'EncryptedDS' configuration from oracle-ds.xml.. (In oracle-ds.xml complete database information is written in text(DatabaseServerIP,Port,Sid,SchemaName,SchemaPassword))...
How can i store encrypted database information in oracle-ds.xml? How my java code will decode that for making jdbc connection?

The SPListItem being updated was not retrieved with all taxonomy fields

I have added document library template using VS 2012. and then added custom content types to this library. the content types includes 3 managed metadata fields. After deploying the project when I add terms to taxonomy fields in document library, I got following error:
"The SPListItem being updated was not retrieved with all taxonomy fields"
I could not find any solution. Any body have an idea whats wrong ?
In the ELEMENTS.XML file of the contenttype your field (actually you need TWO, note how they are linked togheter) must be something like:
<Field Type="Note"
ID="{4B53F593-CF60-40DF-AEAF-23155BB9AA3F}"
DisplayName="_Circular_Tags"
Name="Circular_Tags_NOTE"
StaticName="Circular_Tags_NOTE"
ShowInViewForms="FALSE"
Required="FALSE"
Hidden="TRUE"
CanToggleHidden="TRUE"
RowOrdinal="0">
</Field>
<Field Type="TaxonomyFieldTypeMulti"
ID="{DF553026-F699-456F-AA24-0C6087DBE885}"
Name="Circular_Tags"
StaticName="Circular_Tags"
DisplayName="Circular_Tags_DisplayName"
Description="Circular_Tags_Description"
ShowField="Circular_Tags_Path"
Required="FALSE"
Sortable="FALSE"
AllowDeletion="TRUE"
EnforceUniqueValues="FALSE"
ShowInViewForms="TRUE"
Group="MyContentTypes_Group">
<Default></Default>
<Customization>
<ArrayOfProperty>
<Property>
<Name>TextField</Name>
<Value xmlns:q6="http://www.w3.org/2001/XMLSchema" p4:type="q6:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">{4B53F593-CF60-40DF-AEAF-23155BB9AA3F}</Value>
</Property>
</ArrayOfProperty>
</Customization>
</Field>
Then you need (in the feature-activated code) do the following:
SPSite site = properties.Feature.Parent as SPSite;
Guid fieldId = new Guid("{DF553026-F699-456F-AA24-0C6087DBE885}");
if (site.RootWeb.Fields.Contains(fieldId))
{
TaxonomySession session = new TaxonomySession(site);
if (session.TermStores.Count != 0)
{
var termStore = session.TermStores["ManagedMetadata_Proxy"];
var group = termStore.Groups["GroupName"];
var termSet = group.TermSets["TermSetName"];
TaxonomyField field = site.RootWeb.Fields[fieldId] as TaxonomyField;
//set the text field to the id of the _Circular_Tags field : 4B53F593-CF60-40DF-AEAF-23155BB9AA3F
field.TextField = new Guid("{4B53F593-CF60-40DF-AEAF-23155BB9AA3F}");
// Connect to MMS
field.SspId = termSet.TermStore.Id;
field.TermSetId = termSet.Id;
field.TargetTemplate = string.Empty;
field.AnchorId = Guid.Empty;
field.Update();
}
}
Finally in the SCHEMA.XML file in the List definition you should have a definition like this for the field:
<Field Type="TaxonomyFieldType" ID="{DF553026-F699-456F-AA24-0C6087DBE885}" Name="Circular_Tags" StaticName="Circular_Tags" DisplayName="Circular_Tags_DisplayName" Description="Circular_Tags_Description" ShowField="Circular_Tags_Path" Mult="TRUE" Required="FALSE" Sortable="FALSE" AllowDeletion="TRUE" EnforceUniqueValues="FALSE" ShowInViewForms="TRUE" Group="ContentTypes_Group">
<Default></Default>
<Customization>
<ArrayOfProperty>
<Property>
<Name>TextField</Name>
<Value xmlns:q6="http://www.w3.org/2001/XMLSchema" p4:type="q6:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">{4B53F593-CF60-40DF-AEAF-23155BB9AA3F}</Value>
</Property>
</ArrayOfProperty>
</Customization>
</Field>

Incorrect component when querying immediately after insert using NHibernate

I have the following mapping for my table in MySql:
<class name="Tag, namespace" table="tags" >
<id name="id" type="Int32" unsaved-value="0">
<generator class="native"></generator>
</id>
<property name="name" type="String" not-null="true"></property>
<component name="record_dates" class="DateMetaData, namespace" >
<property name="created_at" type="DateTime" not-null="true"></property>
<property name="updated_at" type="DateTime" not-null="true"></property>
</component>
</class>
As you see the record_dates property is defined as a component field of type DateMetaDate. Both created_at and updated_at fields in 'tags' table are updated via triggers. Thus I can insert a new record like such:
var newTag = new Tag()
{
name = "some string here"
}
Int32 id = (Int32)Session.Save(tag);
Session.Flush();
ITag t = Session.Get<Tag>(id);
ViewData["xxx"] = t.name; // -----> not null
ViewData["xxx"] = t.record_dates.created_at; // -----> is null
However when querying the same record back immediately after it was inserted the record_dates field ends up null even though in the table those fields have got values.
Can any one please point out why the Session.Get ignores getting everything back from the table? is it because it caches the newly created record for which the records_dates is null? If so how can it be told to ignore the cached version?
Try using lazy="false" in your class tag:
<class name="Tag, namespace" table="tags" lazy="false">
As per my knowledge, the default behavior is to have lazy loading, which might not refresh your objects as needed.
The only I so far have found is to call Session.Clear() method on the NHibernate session object, which I guess forces NHibernate to fetch the record from the table again. But I'm afraid it removes everything in cache and thus be inefficient.
You can call ISession.Refresh(obj) to force the object to be reloaded from the database. My understanding is that this will not refresh all relationships, so if you need to reload the complete object graph, call ISession.Evict(obj) then ISession.Get(id) to remove it from the cache and reload it.

Resources