Querying user profile repository ATG - repository

I have added few custom properties on top of ATG's profile. When i want to read the profile values in jsp, i am just importing ATG profile and then accessing the property as profile.name.
I am facing one scenario where i need to return profile.lastName for one type of users and profile.firstName for other type of users. This is based on say profile.userType property.
Is it possible to add the userType check in repository so that when i read profile.name it should return either firstName or lastName.
Since name is referred in many places (1000) i cannot add the user type check everywhere and display name accordingly. So if possible, we can handle this in repo.

Yes you can. Simply use a RepositoryPropertyDescriptor. I hastily cobbled together a version below (not tested but should be enough to get you going):
import atg.repository.RepositoryItem;
import atg.repository.RepositoryItemImpl;
import atg.repository.RepositoryPropertyDescriptor;
public class AcmeRealUserName extends RepositoryPropertyDescriptor{
#Override
public Object getPropertyValue(RepositoryItemImpl profile, Object pValue) {
String userType = (String) profile.getPropertyValue("userType");
String lastName = (String) profile.getPropertyValue("lastName");
String firstName = (String) profile.getPropertyValue("firstName");
if ("firstNameUser".equals(userType)) {
return firstName;
} else {
return lastName;
}
}
#Override
public void setValue(String pAttributeName, Object pValue) {
//Probably Do Nothing since this is a derived property
}
/**
* A class specific logDebug method to output log messages. Unfortunately
* using System.out as the mechanism.
*
* #param pMessage
*/
protected void logDebug(String pMessage) {
System.out.println("### DEBUG(:" + getClass().getName() + getName() + ")" + pMessage);
}
}
You then refer to this new property in your userprofiling.xml as follow:
<property name="name" property-type="com.acme.propertydescriptor.AcmeRealUserName" item-type="string" writable="false" readable="true" />

Related

Is there an automated mechanism where I can detect if any email field is missing?

Let's assume this class:
public class AccountInfo
{
public string Email;
public string Username;
public string Password;
}
and this ASP api:
[HttpPost, Route("create")]
public IActionResult CreateUser([FromBody]AccountInfo Info)
{
...
}
If a user passes something like this:
{
"eail" : "ndienw", <--- notice the mispelling
"username" : "djiw",
"password" : "dow"
}
The email field will be null, so I need in each call to check for every fields.
Is there an automated mechanism where I can detect if any field is missing? I'm looking for something generic that can be applied through all calls.
Being able to opt out and mark some parameters optional would be great, but in our case, everything is always needed so far.
In this scenario, the ModelState is still valid; is that the expected behavior?
You can use data annotations on your Email property. RegularExpression attribute will check the field that located on, whether the value which provided is matching with this pattern. Required attribute checks whether this field is empty or not.
[RegularExpression(#"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*", ErrorMessage = "Email was invalid.")]
[Required]
public string Email;

Native SQL select query using Spring JPA Data Annotation #Query, to cover non-empty, empty and null values at the same time

I have something like this in my repository class in a Spring project:
#Query(value = "SELECT * FROM accounts WHERE (first_name LIKE %:firstName% AND last_name LIKE %:lastName%)", nativeQuery = true)
public List<Account> searchByFirstnameAndLastname(#Param("firstName")String firstName,#Param("lastName")String lastName);
I want it to return everything if the parameters are not provided. Even the ones with null firstname/lastname. And it ignores the null values because of the wildcard used. Since null is different from ''.
I was thinking of an if-statement structure and building the query in runtime based on the provided parameters and then setting the value for the #Query annotation.
I tried generating the where clause and passing it as a parameter but it didn't work. I guess the way Spring Data JPA processes the value of this annotation caused it.
Any idea what is the best solution to this?
Have you tried containing keyword like below :
List<Account> findByFirstnameContainingAndLastNameContaining(String firstName,String lastName);
Docs: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
You cannot go far with #Query
For dynamic queries(with many optional filters), the way to go is using Criteria API or JPQL. I suggest the Criteria API as it is object oriented and suitable for dynamic queries.
I would suggest to use QueryDSL. It is mentioned in the docs JB Nizet already posted. There is is nice but quite old tutorial here.
With QueryDSL it is very convenient to create your queries dynamically and it is easier to understand than the JPA Criteria API.
The only difficulty in using QueryDSL is the need to automatically create the query objects from your entities but this can be automated by using maven.
There are two ways to handle your situation.
The hard way is using RepositoryFactoryBean as follow
create a custom RepositoryFactoryBean
public class DaoRepositoryFactoryBean, T, I extends Serializable>
extends JpaRepositoryFactoryBean
{
#Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager)
{
return new DaoRepositoryFactory(entityManager);
}
private static class DaoRepositoryFactory<E extends AbstractEntity, I extends Serializable> extends JpaRepositoryFactory
{
private EntityManager entityManager;
public DaoRepositoryFactory(EntityManager entityManager)
{
super(entityManager);
this.entityManager = entityManager;
}
#Override
protected Object getTargetRepository(RepositoryMetadata metadata)
{
return new DaoImpl<E>((Class<E>) metadata.getDomainType(), entityManager);
}
#Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata)
{
return Dao.class;
}
}
}
create Dao interface
#NoRepositoryBean
public interface Dao extends JpaRepository
{
List findByParamsOrAllWhenEmpty();
}
create your implementation
#Transactional(readOnly = true)
public class DaoImpl extends SimpleJpaRepository implements Dao
{
private EntityManager entityManager;
public DaoImpl(Class<E> domainClass, EntityManager em)
{
super(domainClass, em);
this.entityManager = em;
this.domainClass = domainClass;
}
List<E> findByParamsOrAllWhenEmpty()
{
//implement your custom query logic
//scan your domainClass methods for Query anotations and do the rest
}
}
introduce it to Spring Jpa Data
jpa:repositories
base-package=""
query-lookup-strategy="" factory-class="com.core.dao.DaoRepositoryFactoryBean"
The easy way using Custom Impl which in this case you can't use #Query annotation.
"coalesce" on MySQL or "IsNull" on SQL Server is my preferred solution. They return back the first non-null value of a list and you may use it as a trick to deal with an empty string just like a null:
#Query(value = "SELECT * FROM accounts WHERE (COALESCE(first_name,'') LIKE %:firstName% AND COALESCE(last_name,'') LIKE %:lastName%)", nativeQuery = true)
public List<Account> searchByFirstnameAndLastname(#Param("firstName")String firstName,#Param("lastName")String lastName);
Thanks to the questioner and the answerer :D at this page:
like '%' does not accept NULL value

SDR: Exclude fields from json for associated object

I have an entities like this:
Restaurant
String name
User manager
User
String name
Date lastLogin
When I do GET /restaurants I want to inline the manager, but only with name, not with lastLogin.
When I do GET /users I want to see the full user: name + lastLogin
So I created a Restaurant-Projection and applied it as excerpt to the RestaurantRepo. But this embeds the full user into the restaurant.
When I create an excerpt project for user too, which omits the lastLogin field then it works for restaurants as expected but not for users because here the lastLogin is missing.
How can I solve this problem?
Thank you
You can do something like this (example from the spring data rest documentation)
#Projection(name = "virtual", types = { Person.class })
public interface VirtualProjection {
#Value("#{target.firstName} #{target.lastName}")
String getFullName();
}
So in your case this would result into this projection:
#Projection(name = "restaurantWithManagerName", types = { Restaurant.class })
public interface RestaurantWithManagerNameProjection {
String getName();
#Value("#{target.manager.name}")
String getManagerName();
}
If you want to keep the orginal structure with a nested User object you could achieve this by creating a custom reduced interface for the user and use it in your projection:
public interface UserLight {
String getName();
}
#Projection(name = "restaurantWithReducedManager", types = Restaurant.class)
public interface RestaurantWithManagerNameProjection {
String getName();
UserLight getManager();
}

Hibernate/Spring taking out class mapping. About reflection

Im trying to write an aplication with uses hibernate to write to database, however in some actions i have to use JDBC on data in tables made by HB.
JDBS is requred to give administrator ability to create SQL queries with will return statistic info about data in database like number of processed document of specified type, numbers of success/failed log in attempts or total value of products in orders.
To do that i've done an from that allows to create class that has override toString() with return nice sql query string.
All works but now im trying to make administrator live easier by hiving him an ability to choose of table/column names. And here is an problem, because they are created by hibernate. some by #column annotation other by field name.
How can i check how field mapping?
I know its all about reflections but didnt do much of that in java yet.
example
#Entity
#Table(name= "my_table_name" )
public class TableOFSomething implements Serializable{
//This field isn't mapped into database and info about it is not requred.
//In fact, info about it may cause an error.
private static final long serialVersionUID = 7L;
#Id
#Column(name="id")
private String id;
private String fieldOne;
#Column(name="field_two")
private String fieldTwo;
#Column(name="renamed_just_for_fun")
private int Number;
//code with getters & setters
}
How to write methods that will have definition like
public <T> String tableName(Class<T> Target); //returns name of table in database
public <T> ArrayList<String> tabelFields(Class<T> Target); //returns name of fields in database
Hibernate has API - getClassMetadata that can explore the mapping. The API might change and is now located in another place , but i will use it and not in reflection for this.
look on this post for more details:
Get the table name from the model in Hibernate
if you want reflection , so use this link
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import javax.persistence.Column;
import javax.persistence.Table;
import odi.beans.statistic.QueryBean;
public class ReflectionTest {
public static void main(String[] args) {
ReflectionTest test=new ReflectionTest();
System.out.println("Table name of "+QueryBean.class.getName()+" is "+test.getTableName(QueryBean.class));
System.out.println("Column names in this table are:");
for(String n: test.getColumnNames(QueryBean.class)){
System.out.println("\t"+n);
}
System.out.println("Good bye ;)");
}
public <T> ArrayList<String> getColumnNames(Class<T> target) {
ArrayList<String> ret=new ArrayList<>();
Field[] fields = target.getDeclaredFields();
String fieldName =null;
for (Field f : fields) {
//jump to next if if field is static
if (Modifier.isStatic(f.getModifiers()))
continue;
if (f.isAnnotationPresent(Column.class)) {
Column a = f.getAnnotation(Column.class);
fieldName = a.name();
} else {
fieldName = f.getName();
}
ret.add(fieldName);
}
return ret;
}
public <T> String getTableName(Class<T> target){
String ret=target.getSimpleName();
if (target.isAnnotationPresent(Table.class))
{
Table t=target.getAnnotation(Table.class);
ret=t.name();
}
return ret;
}
}
Is it cover all possibilities?
I know now that Hibernate way would be easier, but this is also about learning of very useful reflection mechanism :)
EDIT:
Important question:
Will this work only on annotations or also on xml mapping?

How to create a NHibernate proxy object with some initiliazed fields (other than Id)?

I want to create an object proxy, similar to what ISession.Load is returning, but with some fields initialized. For other properties, when accessed, the proxy will fetch the entire object from database.
Consider the following example:
public class User
{
protected User() {
}
public User(int id, string username, string email) {
// ...
}
// initialize the following fields from other datasources
public virtual int Id { get; set; }
public virtual string UserName { get; set; }
public virtual string Email { get; set; }
// the rest of fields when accessed will trigger a select by id in the database
public virtual string Field1 { get; set; }
public virtual string Field2 { get; set; }
public virtual DateTime Field3 { get; set; }
public virtual ISet<Comment> Comments { get; set; }
}
The Id, UserName, Email are well-known in my case, so I could create an object proxy containing these fields, and for the others leave the default proxy behavior. In addition to throwing an exception if this id is not found in the database, i could throw an exception if preinitialized fields do not match or overwrite them silently. I am using NHibernate.ByteCode.Castle for proxy factories.
Edit:
The purpose for this is to be able to have some projection properties from an entity which can be queried elsewhere (say. a lucene index) and to avoid database calls. Then instead of wrapping these fields in a custom component class containing only these subset of properties, I want to use the proxy object directly so that I am able to load the rest of fields if needed. In the best case scenario I wouldn't hit the database at all, but in some corner cases I'd like to access other fields, too. The SELECT N+1 problem's impact can be greatly reduced by using batching.
An hypothetical version of code I want to use would be:
// create User object proxy with some fields initialized
var user = Session.Load<User>(5, new { UserName = "admin", Email = "admin#company.com" });
Console.WriteLine(user.Id); // doesn't hit the database
Console.WriteLine(user.UserName); // doesn't hit the database
Console.WriteLine(user.FullName); // doesn't hit the database
if (somecondition) {
Console.WriteLine(user.Field1); // fetches all other fields
}
You can specify an eager fetch inside the query to actually retrieve the needed associations. This could be done in different ways depending on what query style ( Criteria,Hql or LINQto NH ) you are using. But the key is changing the fetch mode.
for non-collection properties, I wouldn't do that;
the cost of prefetching them from the DB when you load your entity is (usually) so small that I wouldn't even bother.
for collection properties, just mark the collection fetch strategy as 'lazy=true'.
The only case where I would think about doing something like that is when I have a very large number of properties which I don't need (in your example- say Field1..Field20).
In that case I would either:
1. Define those properties together as a component, or
2. create a DTO for fetching only a subset of your entity's properties.
specifying lazy = "true" (or Not.LazyLoad() for Fluent NHib) on properties Field1, Field2, Field3, Comments mappings may help, though not sure about Select N+1 issue.
another way to go is specifying lazy = "false" for UserName, Email