Replacing Hibernate's Criteria API by JPA Criteria API - spring

I have some legacy code written with an old Hibernate 3 version which I'd like to upgrade to 5.3. I am currently working on transitioning from Hibernate Criteria to JPA CriteriaBuilder.
I have something like this:
public List<ENTITY> findByCriteria(final Map<String, Object> criteriaMap, final List<String> fields, final Class<ENTITY> entityClass) {
Session session = this.sessionFactory.getSessionFactory().getCurrentSession();
final Criteria criteria = session.createCriteria(entityClass);
final Set<String> keys = criteriaMap.keySet();
Object object;
CriteriaValue criteriaValue;
CriteriaValue.Operator operator;
for (String key:keys) {
object = criteriaMap.get(key);
if (object instanceof SimpleExpression) {
criteria.add((SimpleExpression)object);
} else if (object instanceof LogicalExpression) {
criteria.add((LogicalExpression)object);
} else if (object instanceof Criterion) {
criteria.add((Criterion)object);
} else if (!isDefaultCriteria(key)) {
if (!(object instanceof CriteriaValue)) {
if (object instanceof String && ((String)object).contains(SYSTEMWILDCARD)) {
criteriaMap.put(key, new CriteriaValue(object, CriteriaValue.Operator.iLIKE));
} else {
criteriaMap.put(key, new CriteriaValue(object, CriteriaValue.Operator.EQ));
}
object = criteriaMap.get(key);
}
operator = ((CriteriaValue)object).getOperator();
criteriaValue = (CriteriaValue)object;
if (criteriaValue != null) {
Object value = criteriaValue.getValue();
switch (operator) {
case EQ:
criteria.add(value == null ? Restrictions.isNull(key) : Restrictions.eq(key, value));
break;
case iLIKE:
criteria.add(Restrictions.ilike(key, cleanWildcards(value)));
break;
case LIKE:
criteria.add(Restrictions.like(key, cleanWildcards(value)));
break;
case LE:
criteria.add(Restrictions.le(key, value));
break;
case GE:
criteria.add(Restrictions.ge(key, value));
break;
case LT:
criteria.add(Restrictions.lt(key, value));
break;
case GT:
criteria.add(Restrictions.gt(key, value));
break;
case OR:
criteria.add(getOrRestrictions(key, value));
break;
case AND:
criteria.add(getAndRestrictions(key, value));
break;
case IN:
criteria.add(Restrictions.in(key, (Object[])value));
break;
case NE:
criteria.add(value == null ? Restrictions.isNotNull(key): Restrictions.ne(key, value));
break;
}
}
}
}
....
}
Which I would rewrite like that:
CriteriaBuilder cb = sessionFactory.getCriteriaBuilder();
CriteriaQuery<?> cq = null;
try {
cq = cb.createQuery(Class.forName(entityClass.getName()));
cq.from(Class.forName(entityClass.getName()));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
List<?> books = sessionFactory.getCurrentSession().createQuery(cq).getResultList();
But my problem comes with the part where the Expressions and Restrictions are added to the criteria, as a corresponding .add() method is not available. How do I rewrite this segment?

You build a list of javax.persistence.criteria.Predicate and in the end use cb.and( predicateList ) to build a final predicate which you can add to the query by passing it to cq.where()

Related

Cannot invoke "org.apache.commons.logging.Log.isDebugEnabled()" because "this.logger" is null

Here the code that I used to save data and I use #Transactional. I want to write a test for this method, but it fails.
#Transactional(rollbackFor = BadRequestException.class, propagation = Propagation.REQUIRES_NEW)
public void saveContent(ContentAbstractEntity content) {
mongoTemplate.setSessionSynchronization(SessionSynchronization.ALWAYS);
TransactionBody txnBody =
(() -> {
ContentAbstractEntity contentAbstractEntity = contentRepository.save(content);
if (content instanceof LearningContent) {
LearningContent learningContent = (LearningContent) content;
Long tutorCountInDB = tutorRepository.countBy_idIn(learningContent.getTutorIds());
if (!countryRepository.existsBySyllabuses_grades_subjects__id(
learningContent.getSubjectId()))
throw new BadRequestException("Subject is not found");
Topic topic = topicRepository.findBy_id(learningContent.getTopicId());
if (topic == null) throw new BadRequestException("Topic is not found");
List<ObjectId> lessonIds =
topic.getLessons().stream().map(m -> m.get_id()).collect(Collectors.toList());
boolean allLessonsExists =
learningContent.getLessonIds().stream().allMatch(a -> lessonIds.contains(a));
if (!allLessonsExists) throw new BadRequestException("Lessons are not mismatched");
if (tutorCountInDB != learningContent.getTutorIds().size()) {
throw new BadRequestException("Tutors are not matched");
}
}
return "Successfully inserted";
});
MongoClient client = MongoClients.create(uri);
ClientSession clientSession = client.startSession();
try {
clientSession.withTransaction(txnBody);
} catch (BadRequestException e) {
throw new BadRequestException(e.getMessage());
} finally {
clientSession.close();
}
}
I tried to write a test for the above code, but it returns an error
#Test
void whenSaveContent() throws Exception {
ContentAbstractDto contentAbstractDto = new ContentAbstractDto();
ContentAbstractEntity content = learningVideo();
if (contentAbstractDto instanceof LearningDocsDto) {
LearningDocsDto learninfDocsDto = (LearningDocsDto) contentAbstractDto;
// ToDo when a Doc Saves and Assessment saves
}
when(contentRepository.save(content)).thenReturn(content);
if (content instanceof LearningContent) {
LearningContent learningContent = (LearningContent) content;
Long tutorCountInDB = (long) learningContent.getTutorIds().size();
when(tutorRepository.countBy_idIn(learningContent.getTutorIds())).thenReturn(tutorCountInDB);
when(subjectRepository.existsBy_id(learningContent.getSubjectId())).thenReturn(true);
Topic topic = getTopic();
when(topicRepository.findBy_id(learningContent.getTopicId())).thenReturn(topic);
if (topic == null) throw new BadRequestException("Topic is not found");
List<ObjectId> lessonIds =
topic.getLessons().stream().map(m -> m.get_id()).collect(Collectors.toList());
boolean allLessonsExists =
learningContent.getLessonIds().stream().allMatch(a -> lessonIds.contains(a));
if (!allLessonsExists) throw new BadRequestException("Lessons are not mismatched");
if (tutorCountInDB != learningContent.getTutorIds().size()) {
throw new BadRequestException("Tutors are not matched");
}
}

How to retrieve filtered value from a stream and use it

I'm new to Java 8:
I have to convert this piece of java 6 code to java 8 version:
List<String> unvalidnumbers = new ArrayList<>();
StringBuilder content = new StringBuilder();
String str = "current_user"
for (Iterator<String> it = numbers.iterator(); it.hasNext();) {
String number = it.next();
try {
PersIdentifier persIdentifier = this.getPersIdentifierByNumber(number);
if (persIdentifier != null) {
content.append(number).append(";").append(str);
if (StringUtils.equals(persIdentifier.getType(), "R")) {
content.append(";X");
}
if (it.hasNext()) {
content.append("\r\n");
}
}
} catch (BusException e) {
LOGGER.warn("Pers not found", e);
unvalidnumbers.add(number);
}
}
So i wrote this:
numbers.stream().filter((String number) -> {
try {
return this.getPersIdentifierByNumber(number) != null;
} catch (BusinessException e1) {
LOGGER.warn("Pers not found", e1);
return false;
}
}).forEach(number -> contentConstruction.append(number).append(";").append(str));
I know it's missing this part:
if (StringUtils.equals(persIdentifier.getType(), "R")) {
content.append(";X");
}
if (it.hasNext()) {
content.append("\r\n");
}
But i didn't found way to retrieve the corresponding persIdentifier object.
Any idea please
You should use a more functional approach if you use Java 8.
Instead of a forEach, favor collect(). Here Collectors.joining() looks suitable.
Not tested but you should have an overall idea :
String result =
numbers.stream()
.map(number -> new SimpleEntry<String, PersIdentifier>(number, this.getPersIdentifierByNumber(number) )
.filter(entry -> entry.getValue() != null)
.map(entry ->{
final String base = entry.getKey() + ";" + str;
return "R".equals(entry.getValue().getType()) ? base + ";X" : base;
})
.collect(joining("\r\n")); // Or not OS dependent : System.lineSeparator()

How to translate Linq Expression where clause to hql where clause?

For some reason, I need to combine Linq Expression (only where clause) & an HQL where clause into one query.
I find that the session.Query<T>() API will translate Linq Expression to a HqlQuery object (that extends HqlExpression).
How can I translate the Linq Expression where clause to an HQL where clause queryString, and then I can combine another HQL where clause queryString into a new query?
Seems that is not possible to use exists NHibernate API to convert Linq expression to HQL tree.
The HQL tree produced from the Linq expression is not reversable to an actual HQL query.
So I have to translate Linq expression to HQL by self:
var expr = GetExpr<Ninja>(x =>
x.Age > 1 && x.Country.Name == "中国"
||
(x.Id > 10 && x.Country.Name == "中国")
);
var translator = new ExpressionToHqlTranslator("_this");
translator.Translate(expr);
Console.WriteLine(translator.WhereClause);
Console.WriteLine(translator.Patameters);
============== result =============
WhereClause: (((_this.Age > ?) AND (_this.Country.Name = ?)) OR ((_this.Id > ?) AND (_this.Country.Name = ?)))
Patameters:4
=============== the critical code =============
static Expression<Func<T, object>> GetExpr<T>(Expression<Func<T, object>> expr){
eturn expr;
}
using System;
using System.Linq;
using NHibernate.Linq;
using NHibernate.Linq.Visitors;
using System.Linq.Expressions;
using NHibernate;
using System.Text;
using System.Collections.Generic;
namespace Rhythm.Linq
{
public class ExpressionToHqlTranslator : System.Linq.Expressions.ExpressionVisitor
{
private StringBuilder sb;
private string _orderBy = "";
private int? _skip = null;
private int? _take = null;
private string _whereClause = "";
List<object> patameters;
public int? Skip
{
get
{
return _skip;
}
}
public int? Take
{
get
{
return _take;
}
}
public string OrderBy
{
get
{
return _orderBy;
}
}
public string WhereClause
{
get
{
return _whereClause;
}
}
public List<object> Patameters
{
get
{
return patameters;
}
set
{
patameters = value;
}
}
string prefix;
public ExpressionToHqlTranslator(string prefix = null)
{
this.prefix = string.IsNullOrEmpty(prefix) ? null : (prefix + ".");
}
public string Translate(Expression expression)
{
this.sb = new StringBuilder();
this.patameters = new List<object>();
this.Visit(expression);
_whereClause = this.sb.ToString();
return _whereClause;
}
private static Expression StripQuotes(Expression e)
{
while (e.NodeType == ExpressionType.Quote)
{
e = ((UnaryExpression)e).Operand;
}
return e;
}
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Where")
{
this.Visit(m.Arguments[0]);
LambdaExpression lambda = (LambdaExpression)StripQuotes(m.Arguments[1]);
this.Visit(lambda.Body);
return m;
}
else if (m.Method.Name == "Take")
{
if (this.ParseTakeExpression(m))
{
Expression nextExpression = m.Arguments[0];
return this.Visit(nextExpression);
}
}
else if (m.Method.Name == "Skip")
{
if (this.ParseSkipExpression(m))
{
Expression nextExpression = m.Arguments[0];
return this.Visit(nextExpression);
}
}
else if (m.Method.Name == "OrderBy")
{
if (this.ParseOrderByExpression(m, "ASC"))
{
Expression nextExpression = m.Arguments[0];
return this.Visit(nextExpression);
}
}
else if (m.Method.Name == "OrderByDescending")
{
if (this.ParseOrderByExpression(m, "DESC"))
{
Expression nextExpression = m.Arguments[0];
return this.Visit(nextExpression);
}
}
throw new NotSupportedException(string.Format("The method '{0}' is not supported", m.Method.Name));
}
protected override Expression VisitUnary(UnaryExpression u)
{
switch (u.NodeType)
{
case ExpressionType.Not:
sb.Append(" NOT ");
this.Visit(u.Operand);
break;
case ExpressionType.Convert:
this.Visit(u.Operand);
break;
default:
throw new NotSupportedException(string.Format("The unary operator '{0}' is not supported", u.NodeType));
}
return u;
}
/// <summary>
///
/// </summary>
/// <param name="b"></param>
/// <returns></returns>
protected override Expression VisitBinary(BinaryExpression b)
{
sb.Append("(");
this.Visit(b.Left);
switch (b.NodeType)
{
case ExpressionType.And:
sb.Append(" AND ");
break;
case ExpressionType.AndAlso:
sb.Append(" AND ");
break;
case ExpressionType.Or:
sb.Append(" OR ");
break;
case ExpressionType.OrElse:
sb.Append(" OR ");
break;
case ExpressionType.Equal:
if (IsNullConstant(b.Right))
{
sb.Append(" IS ");
}
else
{
sb.Append(" = ");
}
break;
case ExpressionType.NotEqual:
if (IsNullConstant(b.Right))
{
sb.Append(" IS NOT ");
}
else
{
sb.Append(" <> ");
}
break;
case ExpressionType.LessThan:
sb.Append(" < ");
break;
case ExpressionType.LessThanOrEqual:
sb.Append(" <= ");
break;
case ExpressionType.GreaterThan:
sb.Append(" > ");
break;
case ExpressionType.GreaterThanOrEqual:
sb.Append(" >= ");
break;
default:
throw new NotSupportedException(string.Format("The binary operator '{0}' is not supported", b.NodeType));
}
this.Visit(b.Right);
sb.Append(")");
return b;
}
protected override Expression VisitConstant(ConstantExpression c)
{
this.patameters.Add(c.Value);
sb.Append('?');
//IQueryable q = c.Value as IQueryable;
//if (q == null && c.Value == null)
//{
// sb.Append("NULL");
//}
//else if (q == null)
//{
// switch (Type.GetTypeCode(c.Value.GetType()))
// {
// case TypeCode.Boolean:
// sb.Append(((bool)c.Value) ? 1 : 0);
// break;
// case TypeCode.String:
// sb.Append("'");
// sb.Append(c.Value);
// sb.Append("'");
// break;
// case TypeCode.DateTime:
// sb.Append("'");
// sb.Append(c.Value);
// sb.Append("'");
// break;
// case TypeCode.Object:
// throw new NotSupportedException(string.Format("The constant for '{0}' is not supported", c.Value));
// default:
// sb.Append(c.Value);
// break;
// }
//}
return c;
}
protected override Expression VisitMember(MemberExpression m)
{
if (this.prefix != null)
{
sb.Append(this.prefix);
}
sb.Append(ContactModelPropertyVistHierarchyExpression(m, m.Member.DeclaringType));
//if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter)
//{
// sb.Append(m.Member.Name);
// return m;
//}
return m;
//throw new NotSupportedException(string.Format("The member '{0}' is not supported", m.Member.Name));
}
protected bool IsNullConstant(Expression exp)
{
return (exp.NodeType == ExpressionType.Constant && ((ConstantExpression)exp).Value == null);
}
private bool ParseOrderByExpression(MethodCallExpression expression, string order)
{
UnaryExpression unary = (UnaryExpression)expression.Arguments[1];
LambdaExpression lambdaExpression = (LambdaExpression)unary.Operand;
lambdaExpression = (LambdaExpression)NHibernate.Linq.Visitors.Evaluator.PartialEval(lambdaExpression);
MemberExpression body = lambdaExpression.Body as MemberExpression;
if (body != null)
{
if (string.IsNullOrEmpty(_orderBy))
{
_orderBy = string.Format("{0} {1}", body.Member.Name, order);
}
else
{
_orderBy = string.Format("{0}, {1} {2}", _orderBy, body.Member.Name, order);
}
return true;
}
return false;
}
private bool ParseTakeExpression(MethodCallExpression expression)
{
ConstantExpression sizeExpression = (ConstantExpression)expression.Arguments[1];
int size;
if (int.TryParse(sizeExpression.Value.ToString(), out size))
{
_take = size;
return true;
}
return false;
}
private bool ParseSkipExpression(MethodCallExpression expression)
{
ConstantExpression sizeExpression = (ConstantExpression)expression.Arguments[1];
int size;
if (int.TryParse(sizeExpression.Value.ToString(), out size))
{
_skip = size;
return true;
}
return false;
}
}
public static string ContactModelPropertyVistHierarchyExpression(Expression expr, Type modelType)
{
StringBuilder sb = new StringBuilder();
Expression curr = expr;
// TypedParameterExpression
while (curr != null)
{
if (curr is MemberExpression)
{
var x = curr as MemberExpression;
sb.Insert(0, x.Member.Name);
curr = x.Expression;
}
else if (curr is MethodCallExpression)
{
var x = curr as MethodCallExpression;
sb.Insert(0, x.Method.Name);
curr = x.Object;
}
else if (curr is ParameterExpression)
{
break;
}
else
{
throw new ArgumentException("Unsupported Expression type " + curr.GetType().FullName + " for expression " + expr.ToString(), "expr");
}
sb.Insert(0, '.');
}
return sb.Length > 1 ? sb.Remove(0, 1).ToString() : sb.ToString();
}
}
add dll reference NHibernate.Linq.dll

Spring JPA Specifications Not In Query

Here the IN criteria query using Spring JPA Specification is working fine. but I don't know how to use "NOT IN"..
So How can i use the NOT IN criteria query using Spring JPA Specifications.
SearchSpecification<User> spec = new CommonSpecification<Case>(new SearchCriteria("client.id", Operator.NOT_IN, clientIdList));
#Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Predicate predicate = null;
switch (searchCriteria.getOperator()) {
case IN:
if (searchCriteria.getValue() instanceof List<?>) {
predicate = getFieldPath(searchCriteria.getKey(), root)
.in(((List<?>) searchCriteria.getValue()).toArray());
}
break;
case NOT_IN:
//What to do???
break;
default:
break;
}
return predicate;
}
private Path<Object> getFieldPath(String key, Root<T> root) {
Path<Object> fieldPath = null;
if (key.contains(".")) {
String[] fields = key.split("\\.");
for (String field : fields) {
if (fieldPath == null) {
fieldPath = root.get(field);
} else {
fieldPath = fieldPath.get(field);
}
}
} else {
fieldPath = root.get(key);
}
return fieldPath;
}
Use the Predicate.not() method; the code is the same as in the IN case, just add .not():
case NOT_IN:
if (searchCriteria.getValue() instanceof List<?>) {
predicate = getFieldPath(searchCriteria.getKey(), root)
.in(((List<?>) searchCriteria.getValue()).toArray())
.not();
}
break;
This functionality also works fine..
case NOT_IN:
if (searchCriteria.getValue() instanceof List<?>) {
predicate = criteriaBuilder.not(getFieldPath(searchCriteria.getKey(), root).in(((List<?>) searchCriteria.getValue()).toArray()));
}
break;

Reading a file with newlines as a tuple in pig

Is it possible to change the record delimiter from newline to some other string so as to read a file with newlines into a single tuple in pig.
Yes.
A = LOAD '...' USING PigStorage(',') AS (...); //comma is the delimeter for fields
SET textinputformat.record.delimiter '<delimeter>'; // record delimeter, by default it is `\n`. You can change to any delimeter.
As mentioned here
You can use PigStorage
A = LOAD '/some/path/COMMA-DELIM-PREFIX*' USING PigStorage(',') AS (f1:chararray, ...);
B = LOAD '/some/path/SEMICOLON-DELIM-PREFIX*' USING PigStorage('\t') AS (f1:chararray, ...);
You can even try writing load/store UDF.
There is java code example for both load and store.
Load Functions : LoadFunc abstract class has the main methods for loading data and for most use cases it would suffice to extend it. You can read more here
Example
The loader implementation in the example is a loader for text data
with line delimiter as '\n' and '\t' as default field delimiter (which
can be overridden by passing a different field delimiter in the
constructor) - this is similar to current PigStorage loader in Pig.
The implementation uses an existing Hadoop supported Inputformat -
TextInputFormat - as the underlying InputFormat.
public class SimpleTextLoader extends LoadFunc {
protected RecordReader in = null;
private byte fieldDel = '\t';
private ArrayList<Object> mProtoTuple = null;
private TupleFactory mTupleFactory = TupleFactory.getInstance();
private static final int BUFFER_SIZE = 1024;
public SimpleTextLoader() {
}
/**
* Constructs a Pig loader that uses specified character as a field delimiter.
*
* #param delimiter
* the single byte character that is used to separate fields.
* ("\t" is the default.)
*/
public SimpleTextLoader(String delimiter) {
this();
if (delimiter.length() == 1) {
this.fieldDel = (byte)delimiter.charAt(0);
} else if (delimiter.length() > 1 & & delimiter.charAt(0) == '\\') {
switch (delimiter.charAt(1)) {
case 't':
this.fieldDel = (byte)'\t';
break;
case 'x':
fieldDel =
Integer.valueOf(delimiter.substring(2), 16).byteValue();
break;
case 'u':
this.fieldDel =
Integer.valueOf(delimiter.substring(2)).byteValue();
break;
default:
throw new RuntimeException("Unknown delimiter " + delimiter);
}
} else {
throw new RuntimeException("PigStorage delimeter must be a single character");
}
}
#Override
public Tuple getNext() throws IOException {
try {
boolean notDone = in.nextKeyValue();
if (!notDone) {
return null;
}
Text value = (Text) in.getCurrentValue();
byte[] buf = value.getBytes();
int len = value.getLength();
int start = 0;
for (int i = 0; i < len; i++) {
if (buf[i] == fieldDel) {
readField(buf, start, i);
start = i + 1;
}
}
// pick up the last field
readField(buf, start, len);
Tuple t = mTupleFactory.newTupleNoCopy(mProtoTuple);
mProtoTuple = null;
return t;
} catch (InterruptedException e) {
int errCode = 6018;
String errMsg = "Error while reading input";
throw new ExecException(errMsg, errCode,
PigException.REMOTE_ENVIRONMENT, e);
}
}
private void readField(byte[] buf, int start, int end) {
if (mProtoTuple == null) {
mProtoTuple = new ArrayList<Object>();
}
if (start == end) {
// NULL value
mProtoTuple.add(null);
} else {
mProtoTuple.add(new DataByteArray(buf, start, end));
}
}
#Override
public InputFormat getInputFormat() {
return new TextInputFormat();
}
#Override
public void prepareToRead(RecordReader reader, PigSplit split) {
in = reader;
}
#Override
public void setLocation(String location, Job job)
throws IOException {
FileInputFormat.setInputPaths(job, location);
}
}
Store Functions : StoreFunc abstract class has the main methods for storing data and for most use cases it should suffice to extend it
Example
The storer implementation in the example is a storer for text data
with line delimiter as '\n' and '\t' as default field delimiter (which
can be overridden by passing a different field delimiter in the
constructor) - this is similar to current PigStorage storer in Pig.
The implementation uses an existing Hadoop supported OutputFormat -
TextOutputFormat as the underlying OutputFormat.
public class SimpleTextStorer extends StoreFunc {
protected RecordWriter writer = null;
private byte fieldDel = '\t';
private static final int BUFFER_SIZE = 1024;
private static final String UTF8 = "UTF-8";
public PigStorage() {
}
public PigStorage(String delimiter) {
this();
if (delimiter.length() == 1) {
this.fieldDel = (byte)delimiter.charAt(0);
} else if (delimiter.length() > 1delimiter.charAt(0) == '\\') {
switch (delimiter.charAt(1)) {
case 't':
this.fieldDel = (byte)'\t';
break;
case 'x':
fieldDel =
Integer.valueOf(delimiter.substring(2), 16).byteValue();
break;
case 'u':
this.fieldDel =
Integer.valueOf(delimiter.substring(2)).byteValue();
break;
default:
throw new RuntimeException("Unknown delimiter " + delimiter);
}
} else {
throw new RuntimeException("PigStorage delimeter must be a single character");
}
}
ByteArrayOutputStream mOut = new ByteArrayOutputStream(BUFFER_SIZE);
#Override
public void putNext(Tuple f) throws IOException {
int sz = f.size();
for (int i = 0; i < sz; i++) {
Object field;
try {
field = f.get(i);
} catch (ExecException ee) {
throw ee;
}
putField(field);
if (i != sz - 1) {
mOut.write(fieldDel);
}
}
Text text = new Text(mOut.toByteArray());
try {
writer.write(null, text);
mOut.reset();
} catch (InterruptedException e) {
throw new IOException(e);
}
}
#SuppressWarnings("unchecked")
private void putField(Object field) throws IOException {
//string constants for each delimiter
String tupleBeginDelim = "(";
String tupleEndDelim = ")";
String bagBeginDelim = "{";
String bagEndDelim = "}";
String mapBeginDelim = "[";
String mapEndDelim = "]";
String fieldDelim = ",";
String mapKeyValueDelim = "#";
switch (DataType.findType(field)) {
case DataType.NULL:
break; // just leave it empty
case DataType.BOOLEAN:
mOut.write(((Boolean)field).toString().getBytes());
break;
case DataType.INTEGER:
mOut.write(((Integer)field).toString().getBytes());
break;
case DataType.LONG:
mOut.write(((Long)field).toString().getBytes());
break;
case DataType.FLOAT:
mOut.write(((Float)field).toString().getBytes());
break;
case DataType.DOUBLE:
mOut.write(((Double)field).toString().getBytes());
break;
case DataType.BYTEARRAY: {
byte[] b = ((DataByteArray)field).get();
mOut.write(b, 0, b.length);
break;
}
case DataType.CHARARRAY:
// oddly enough, writeBytes writes a string
mOut.write(((String)field).getBytes(UTF8));
break;
case DataType.MAP:
boolean mapHasNext = false;
Map<String, Object> m = (Map<String, Object>)field;
mOut.write(mapBeginDelim.getBytes(UTF8));
for(Map.Entry<String, Object> e: m.entrySet()) {
if(mapHasNext) {
mOut.write(fieldDelim.getBytes(UTF8));
} else {
mapHasNext = true;
}
putField(e.getKey());
mOut.write(mapKeyValueDelim.getBytes(UTF8));
putField(e.getValue());
}
mOut.write(mapEndDelim.getBytes(UTF8));
break;
case DataType.TUPLE:
boolean tupleHasNext = false;
Tuple t = (Tuple)field;
mOut.write(tupleBeginDelim.getBytes(UTF8));
for(int i = 0; i < t.size(); ++i) {
if(tupleHasNext) {
mOut.write(fieldDelim.getBytes(UTF8));
} else {
tupleHasNext = true;
}
try {
putField(t.get(i));
} catch (ExecException ee) {
throw ee;
}
}
mOut.write(tupleEndDelim.getBytes(UTF8));
break;
case DataType.BAG:
boolean bagHasNext = false;
mOut.write(bagBeginDelim.getBytes(UTF8));
Iterator<Tuple> tupleIter = ((DataBag)field).iterator();
while(tupleIter.hasNext()) {
if(bagHasNext) {
mOut.write(fieldDelim.getBytes(UTF8));
} else {
bagHasNext = true;
}
putField((Object)tupleIter.next());
}
mOut.write(bagEndDelim.getBytes(UTF8));
break;
default: {
int errCode = 2108;
String msg = "Could not determine data type of field: " + field;
throw new ExecException(msg, errCode, PigException.BUG);
}
}
}
#Override
public OutputFormat getOutputFormat() {
return new TextOutputFormat<WritableComparable, Text>();
}
#Override
public void prepareToWrite(RecordWriter writer) {
this.writer = writer;
}
#Override
public void setStoreLocation(String location, Job job) throws IOException {
job.getConfiguration().set("mapred.textoutputformat.separator", "");
FileOutputFormat.setOutputPath(job, new Path(location));
if (location.endsWith(".bz2")) {
FileOutputFormat.setCompressOutput(job, true);
FileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class);
} else if (location.endsWith(".gz")) {
FileOutputFormat.setCompressOutput(job, true);
FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
}
}
}

Resources